diff options
137 files changed, 5075 insertions, 2970 deletions
diff --git a/Android.bp b/Android.bp index 970d66b6144d..dd49a67a79cf 100644 --- a/Android.bp +++ b/Android.bp @@ -25,8 +25,8 @@ // // READ ME: ######################################################## -java_library { - name: "framework", +java_defaults { + name: "framework-defaults", installable: true, srcs: [ @@ -703,12 +703,38 @@ java_library { "libmedia2_jni", ], - javac_shard_size: 150, - dxflags: [ "--core-library", "--multi-dex", ], + +} + +java_library { + name: "framework", + defaults: ["framework-defaults"], + javac_shard_size: 150, +} + +java_library { + name: "framework-annotation-proc", + defaults: ["framework-defaults"], + // Use UsedByApps annotation processor + annotation_processors: ["unsupportedappusage-annotation-processor"], + // b/25860419: annotation processors must be explicitly specified for grok + annotation_processor_classes: [ + "android.processor.unsupportedappusage.UsedByAppsProcessor", + ], +} + +// A host library including just UnsupportedAppUsage.java so that the annotation +// processor can also use this annotation. +java_library_host { + name: "unsupportedappusage-annotation", + srcs: [ + "core/java/android/annotation/IntDef.java", + "core/java/android/annotation/UnsupportedAppUsage.java", + ], } // A temporary build target that is conditionally included on the bootclasspath if diff --git a/api/current.txt b/api/current.txt index f750e33a9610..ed5451b75806 100755 --- a/api/current.txt +++ b/api/current.txt @@ -11313,6 +11313,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin"; field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded"; field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet"; + field public static final java.lang.String FEATURE_FACE = "android.hardware.face"; field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch"; field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct"; field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand"; @@ -11322,6 +11323,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors"; field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen"; field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods"; + field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris"; field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback"; field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only"; field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv"; @@ -13784,7 +13786,7 @@ package android.graphics { field public static final int YV12 = 842094169; // 0x32315659 } - public class Insets { + public final class Insets { method public static android.graphics.Insets of(int, int, int, int); method public static android.graphics.Insets of(android.graphics.Rect); field public static final android.graphics.Insets NONE; @@ -13984,6 +13986,8 @@ package android.graphics { method public float getRunAdvance(char[], int, int, int, int, boolean, int); method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int); method public android.graphics.Shader getShader(); + method public float getStrikeThruPosition(); + method public float getStrikeThruThickness(); method public android.graphics.Paint.Cap getStrokeCap(); method public android.graphics.Paint.Join getStrokeJoin(); method public float getStrokeMiter(); @@ -14004,6 +14008,9 @@ package android.graphics { method public int getTextWidths(java.lang.String, int, int, float[]); method public int getTextWidths(java.lang.String, float[]); method public android.graphics.Typeface getTypeface(); + method public float getUnderlinePosition(); + method public float getUnderlineThickness(); + method public float getWordSpacing(); method public android.graphics.Xfermode getXfermode(); method public boolean hasGlyph(java.lang.String); method public final boolean isAntiAlias(); @@ -14055,6 +14062,7 @@ package android.graphics { method public void setTextSkewX(float); method public android.graphics.Typeface setTypeface(android.graphics.Typeface); method public void setUnderlineText(boolean); + method public void setWordSpacing(float); method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode); field public static final int ANTI_ALIAS_FLAG = 1; // 0x1 field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100 @@ -15135,9 +15143,9 @@ package android.graphics.drawable { public class StateListDrawable extends android.graphics.drawable.DrawableContainer { ctor public StateListDrawable(); method public void addState(int[], android.graphics.drawable.Drawable); + method public int findStateDrawableIndex(int[]); method public int getStateCount(); method public android.graphics.drawable.Drawable getStateDrawable(int); - method public int getStateDrawableIndex(int[]); method public int[] getStateSet(int); } diff --git a/api/system-current.txt b/api/system-current.txt index 1d5f586ab5e9..afce431d49ec 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -474,9 +474,6 @@ package android.app { method public android.app.Vr2dDisplayProperties.Builder setEnabled(boolean); } - public static abstract class Vr2dDisplayProperties.Vr2dDisplayFlag implements java.lang.annotation.Annotation { - } - public class VrManager { method public int getVr2dDisplayId(); method public boolean isPersistentVrModeEnabled(); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 3c9f7eee10b6..2ecfbe733bca 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -133,7 +133,7 @@ message Atom { } // Pulled events will start at field 10000. - // Next: 10024 + // Next: 10025 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -159,6 +159,7 @@ message Atom { Temperature temperature = 10021; BinderCalls binder_calls = 10022; BinderCallsExceptions binder_calls_exceptions = 10023; + LooperStats looper_stats = 10024; } // DO NOT USE field numbers above 100,000 in AOSP. Field numbers above @@ -2203,3 +2204,50 @@ message BinderCallsExceptions { // Total number of exceptions. optional int64 exception_count = 2; } + +message LooperStats { + // Currently not collected and always set to 0. + optional int32 uid = 1 [(is_uid) = true]; + + // Fully qualified class name of the handler target class. + // + // This field does not contain PII. This is a system server class name. + optional string handler_class_name = 2; + + // The name of the thread that runs the Looper. + // + // This field does not contain PII. This is a system server thread name. + optional string looper_thread_name = 3; + + // The name of the dispatched message. + // + // This field does not contain PII. This is a system server constant or class + // name. + optional string message_name = 4; + + // Total number of successfully dispatched messages. + optional int64 message_count = 5; + + // Total number of messages that failed dispatching. + optional int64 exception_count = 6; + + // Total number of processed messages we have data recorded for. If we + // collected data for all the messages, message_count will be equal to + // recorded_message_count. + // + // If recorded_message_count is different than message_count, it means data + // collection has been sampled. All the fields below will be sampled in this + // case. + optional int64 recorded_message_count = 7; + + // Total latency of all processed messages. + // Average can be computed using recorded_total_latency_micros / + // recorded_message_count. + optional int64 recorded_total_latency_micros = 8; + + // Total CPU usage of all processed message. + // Average can be computed using recorded_total_cpu_micros / + // recorded_message_count. Total can be computed using + // recorded_total_cpu_micros / recorded_message_count * call_count. + optional int64 recorded_total_cpu_micros = 9; +} diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp index e44351b39769..42cc543f18a8 100644 --- a/cmds/statsd/src/external/Perfetto.cpp +++ b/cmds/statsd/src/external/Perfetto.cpp @@ -105,9 +105,9 @@ bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config, readPipe.reset(); // Close the read end (owned by the child process). - // Using fopen() because fwrite() has the right logic to chunking write() + // Using fdopen() because fwrite() has the right logic to chunking write() // over a pipe (see __sfvwrite()). - FILE* writePipeStream = fdopen(writePipe.get(), "wb"); + FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb"); if (!writePipeStream) { ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno)); return false; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index e6e84550cf5f..28718823bb32 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -183,7 +183,13 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{}, {}, 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}} + new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}, + // looper_stats + {android::util::LOOPER_STATS, + {{5, 6, 7, 8, 9}, + {2, 3, 4}, + 1 * NS_PER_SEC, + new StatsCompanionServicePuller(android::util::LOOPER_STATS)}} }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/core/java/android/app/Vr2dDisplayProperties.java b/core/java/android/app/Vr2dDisplayProperties.java index 2fd82b2d29e2..17521319aaf8 100644 --- a/core/java/android/app/Vr2dDisplayProperties.java +++ b/core/java/android/app/Vr2dDisplayProperties.java @@ -35,6 +35,7 @@ public final class Vr2dDisplayProperties implements Parcelable { public static final int FLAG_VIRTUAL_DISPLAY_ENABLED = 1; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ FLAG_VIRTUAL_DISPLAY_ENABLED diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7d8ff4c3d0c8..5b0e85632b90 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2274,7 +2274,6 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication. - * @hide */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_FACE = "android.hardware.face"; @@ -2282,7 +2281,6 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication. - * @hide */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_IRIS = "android.hardware.iris"; diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 57ec17842ff1..121b43275257 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -2076,23 +2076,23 @@ public final class Configuration implements Parcelable, Comparable<Configuration break; } - switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) { - case Configuration.COLOR_MODE_HDR_YES: - parts.add("highdr"); + switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) { + case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES: + parts.add("widecg"); break; - case Configuration.COLOR_MODE_HDR_NO: - parts.add("lowdr"); + case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO: + parts.add("nowidecg"); break; default: break; } - switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) { - case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES: - parts.add("widecg"); + switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) { + case Configuration.COLOR_MODE_HDR_YES: + parts.add("highdr"); break; - case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO: - parts.add("nowidecg"); + case Configuration.COLOR_MODE_HDR_NO: + parts.add("lowdr"); break; default: break; diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java index b4955ea44288..496f34c0348a 100644 --- a/core/java/android/hardware/display/DisplayViewport.java +++ b/core/java/android/hardware/display/DisplayViewport.java @@ -30,6 +30,12 @@ import android.text.TextUtils; * @hide Only for use within the system server. */ public final class DisplayViewport { + + // Viewport constants defined in InputReader.h. + public static final int VIEWPORT_INTERNAL = 1; + public static final int VIEWPORT_EXTERNAL = 2; + public static final int VIEWPORT_VIRTUAL = 3; + // True if this viewport is valid. public boolean valid; diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 3141be4e9e97..ae0855a684ed 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -300,6 +300,23 @@ public class UsbManager { public static final String EXTRA_PERMISSION_GRANTED = "permission"; /** + * Name of extra added to start systemui.usb.UsbPermissionActivity + * containing package name of the app which requests USB permission. + * + * @hide + */ + public static final String EXTRA_PACKAGE = "android.hardware.usb.extra.PACKAGE"; + + /** + * Name of extra added to start systemui.usb.UsbPermissionActivity + * containing the whether the app which requests USB permission can be set as default handler + * for USB device attach event or USB accessory attach event or not. + * + * @hide + */ + public static final String EXTRA_CAN_BE_DEFAULT = "android.hardware.usb.extra.CAN_BE_DEFAULT"; + + /** * Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)} * {@hide} */ diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 684a8ee43c87..ded3a1983fb2 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -18,7 +18,6 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.LooperProto; import android.util.Log; import android.util.Printer; import android.util.Slog; @@ -70,6 +69,7 @@ public final class Looper { // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class + private static Observer sObserver; final MessageQueue mQueue; final Thread mThread; @@ -131,6 +131,15 @@ public final class Looper { } /** + * Set the transaction observer for all Loopers in this process. + * + * @hide + */ + public static void setObserver(@Nullable Observer observer) { + sObserver = observer; + } + + /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ @@ -169,6 +178,8 @@ public final class Looper { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } + // Make sure the observer won't change while processing a transaction. + final Observer observer = sObserver; final long traceTag = me.mTraceTag; long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; @@ -189,9 +200,21 @@ public final class Looper { final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0; final long dispatchEnd; + Object token = null; + if (observer != null) { + token = observer.messageDispatchStarting(); + } try { msg.target.dispatchMessage(msg); + if (observer != null) { + observer.messageDispatched(token, msg); + } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; + } catch (Exception exception) { + if (observer != null) { + observer.dispatchingThrewException(token, msg, exception); + } + throw exception; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); @@ -397,4 +420,39 @@ public final class Looper { return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; } + + /** {@hide} */ + public interface Observer { + /** + * Called right before a message is dispatched. + * + * <p> The token type is not specified to allow the implementation to specify its own type. + * + * @return a token used for collecting telemetry when dispatching a single message. + * The token token must be passed back exactly once to either + * {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException} + * and must not be reused again. + * + */ + Object messageDispatchStarting(); + + /** + * Called when a message was processed by a Handler. + * + * @param token Token obtained by previously calling + * {@link Observer#messageDispatchStarting} on the same Observer instance. + * @param msg The message that was dispatched. + */ + void messageDispatched(Object token, Message msg); + + /** + * Called when an exception was thrown while processing a message. + * + * @param token Token obtained by previously calling + * {@link Observer#messageDispatchStarting} on the same Observer instance. + * @param msg The message that was dispatched and caused an exception. + * @param exception The exception that was thrown. + */ + void dispatchingThrewException(Object token, Message msg, Exception exception); + } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a1cc67d661e3..71938d219ccc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13308,6 +13308,23 @@ public final class Settings { * @hide */ public static final String BINDER_CALLS_STATS = "binder_calls_stats"; + + /** + * Default user id to boot into. They map to user ids, for example, 10, 11, 12. + * + * @hide + */ + public static final String DEFAULT_USER_ID_TO_BOOT_INTO = "default_boot_into_user_id"; + + /** + * Persistent user id that is last logged in to. + * + * They map to user ids, for example, 10, 11, 12. + * + * @hide + */ + public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id"; + } /** diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index febdb83f0af3..4bd86a44a8ed 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -164,8 +164,7 @@ public abstract class WallpaperService extends Service { int mType; int mCurWidth; int mCurHeight; - int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_SCALED; + int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; int mWindowPrivateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; int mCurWindowFlags = mWindowFlags; @@ -776,6 +775,7 @@ public abstract class WallpaperService extends Service { mDisplay.getDisplayInfo(displayInfo); mLayout.width = Math.max(displayInfo.logicalWidth, myWidth); mLayout.height = Math.max(displayInfo.logicalHeight, myHeight); + mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED; } mLayout.format = mFormat; @@ -785,8 +785,7 @@ public abstract class WallpaperService extends Service { | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_SCALED; + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mCurWindowPrivateFlags = mWindowPrivateFlags; mLayout.privateFlags = mWindowPrivateFlags; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index a7f14b6bf0c6..b5ade2a3b654 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -45,6 +45,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put("settings_dynamic_homepage", "false"); DEFAULT_FLAGS.put("settings_mobile_network_v2", "false"); + DEFAULT_FLAGS.put("settings_data_usage_v2", "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true"); } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 0da47fd802b3..18838090f58b 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -545,7 +545,7 @@ public class PopupWindow { StateListDrawable stateList = (StateListDrawable) mBackground; // Find the above-anchor view - this one's easy, it should be labeled as such. - int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); + int aboveAnchorStateIndex = stateList.findStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); // Now, for the below-anchor view, look for any other drawable specified in the // StateListDrawable which is not for the above-anchor state and use that. diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java index ba6cf269b737..75151806cfcf 100644 --- a/core/java/com/android/internal/app/ColorDisplayController.java +++ b/core/java/com/android/internal/app/ColorDisplayController.java @@ -112,8 +112,8 @@ public final class ColorDisplayController { private final Context mContext; private final int mUserId; - private final ContentObserver mContentObserver; + private ContentObserver mContentObserver; private Callback mCallback; private MetricsLogger mMetricsLogger; @@ -124,18 +124,6 @@ public final class ColorDisplayController { public ColorDisplayController(@NonNull Context context, int userId) { mContext = context.getApplicationContext(); mUserId = userId; - - mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - - final String setting = uri == null ? null : uri.getLastPathSegment(); - if (setting != null) { - onSettingChanged(setting); - } - } - }; } /** @@ -522,6 +510,20 @@ public final class ColorDisplayController { if (oldCallback != callback) { mCallback = callback; + if (mContentObserver == null) { + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + + final String setting = uri == null ? null : uri.getLastPathSegment(); + if (setting != null) { + onSettingChanged(setting); + } + } + }; + } + if (callback == null) { // Stop listening for changes now that there IS NOT a listener. mContext.getContentResolver().unregisterContentObserver(mContentObserver); diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java new file mode 100644 index 000000000000..edf475f1dba3 --- /dev/null +++ b/core/java/com/android/internal/os/LooperStats.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ThreadLocalRandom; + +/** + * Collects aggregated telemetry data about Looper message dispatching. + * + * @hide Only for use within the system server. + */ +public class LooperStats implements Looper.Observer { + private static final int TOKEN_POOL_SIZE = 50; + private static final int DEFAULT_ENTRIES_SIZE_CAP = 2000; + private static final int DEFAULT_SAMPLING_INTERVAL = 100; + + @GuardedBy("mLock") + private final SparseArray<Entry> mEntries = new SparseArray<>(256); + private final Object mLock = new Object(); + private final Entry mOverflowEntry = new Entry("OVERFLOW"); + private final Entry mHashCollisionEntry = new Entry("HASH_COLLISION"); + private final ConcurrentLinkedQueue<DispatchSession> mSessionPool = + new ConcurrentLinkedQueue<>(); + private final int mSamplingInterval; + private final int mEntriesSizeCap; + + public LooperStats() { + this(DEFAULT_SAMPLING_INTERVAL, DEFAULT_ENTRIES_SIZE_CAP); + } + + public LooperStats(int samplingInterval, int entriesSizeCap) { + this.mSamplingInterval = samplingInterval; + this.mEntriesSizeCap = entriesSizeCap; + } + + @Override + public Object messageDispatchStarting() { + if (shouldCollectDetailedData()) { + DispatchSession session = mSessionPool.poll(); + session = session == null ? new DispatchSession() : session; + session.startTimeMicro = getElapsedRealtimeMicro(); + session.cpuStartMicro = getThreadTimeMicro(); + return session; + } + + return DispatchSession.NOT_SAMPLED; + } + + @Override + public void messageDispatched(Object token, Message msg) { + DispatchSession session = (DispatchSession) token; + Entry entry = getOrCreateEntry(msg); + synchronized (entry) { + entry.messageCount++; + if (session != DispatchSession.NOT_SAMPLED) { + entry.recordedMessageCount++; + long latency = getElapsedRealtimeMicro() - session.startTimeMicro; + long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro; + entry.totalLatencyMicro += latency; + entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency); + entry.cpuUsageMicro += cpuUsage; + entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage); + } + } + + recycleSession(session); + } + + @Override + public void dispatchingThrewException(Object token, Message msg, Exception exception) { + DispatchSession session = (DispatchSession) token; + Entry entry = getOrCreateEntry(msg); + synchronized (entry) { + entry.exceptionCount++; + } + recycleSession(session); + } + + /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */ + public List<ExportedEntry> getEntries() { + final ArrayList<ExportedEntry> entries; + synchronized (mLock) { + final int size = mEntries.size(); + entries = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + Entry entry = mEntries.valueAt(i); + synchronized (entry) { + entries.add(new ExportedEntry(entry)); + } + } + } + // Add the overflow and collision entries only if they have any data. + if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) { + synchronized (mOverflowEntry) { + entries.add(new ExportedEntry(mOverflowEntry)); + } + } + if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) { + synchronized (mHashCollisionEntry) { + entries.add(new ExportedEntry(mHashCollisionEntry)); + } + } + return entries; + } + + /** Removes all collected data. */ + public void reset() { + synchronized (mLock) { + mEntries.clear(); + } + synchronized (mHashCollisionEntry) { + mHashCollisionEntry.reset(); + } + synchronized (mOverflowEntry) { + mOverflowEntry.reset(); + } + } + + @NonNull + private Entry getOrCreateEntry(Message msg) { + final int id = Entry.idFor(msg); + Entry entry; + synchronized (mLock) { + entry = mEntries.get(id); + if (entry == null) { + if (mEntries.size() >= mEntriesSizeCap) { + // If over the size cap, track totals under a single entry. + return mOverflowEntry; + } + entry = new Entry(msg); + mEntries.put(id, entry); + } + } + + if (entry.handler.getClass() != msg.getTarget().getClass() + || entry.handler.getLooper().getThread() + != msg.getTarget().getLooper().getThread()) { + // If a hash collision happened, track totals under a single entry. + return mHashCollisionEntry; + } + return entry; + } + + private void recycleSession(DispatchSession session) { + if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) { + mSessionPool.add(session); + } + } + + protected long getThreadTimeMicro() { + return SystemClock.currentThreadTimeMicro(); + } + + protected long getElapsedRealtimeMicro() { + return SystemClock.elapsedRealtimeNanos() / 1000; + } + + protected boolean shouldCollectDetailedData() { + return ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + } + + private static class DispatchSession { + static final DispatchSession NOT_SAMPLED = new DispatchSession(); + public long startTimeMicro; + public long cpuStartMicro; + } + + private static class Entry { + public final Handler handler; + public final String messageName; + public long messageCount; + public long recordedMessageCount; + public long exceptionCount; + public long totalLatencyMicro; + public long maxLatencyMicro; + public long cpuUsageMicro; + public long maxCpuUsageMicro; + + Entry(Message msg) { + handler = msg.getTarget(); + messageName = handler.getMessageName(msg); + } + + Entry(String specialEntryName) { + handler = null; + messageName = specialEntryName; + } + + void reset() { + messageCount = 0; + recordedMessageCount = 0; + exceptionCount = 0; + totalLatencyMicro = 0; + maxLatencyMicro = 0; + cpuUsageMicro = 0; + maxCpuUsageMicro = 0; + } + + static int idFor(Message msg) { + int result = 7; + result = 31 * result + msg.getTarget().getLooper().getThread().hashCode(); + result = 31 * result + msg.getTarget().getClass().hashCode(); + if (msg.getCallback() != null) { + return 31 * result + msg.getCallback().getClass().hashCode(); + } else { + return 31 * result + msg.what; + } + } + } + + /** Aggregated data of Looper message dispatching in the in the current process. */ + public static class ExportedEntry { + public final String handlerClassName; + public final String threadName; + public final String messageName; + public final long messageCount; + public final long recordedMessageCount; + public final long exceptionCount; + public final long totalLatencyMicros; + public final long maxLatencyMicros; + public final long cpuUsageMicros; + public final long maxCpuUsageMicros; + + ExportedEntry(Entry entry) { + if (entry.handler != null) { + this.handlerClassName = entry.handler.getClass().getName(); + this.threadName = entry.handler.getLooper().getThread().getName(); + } else { + // Overflow/collision entries do not have a handler set. + this.handlerClassName = ""; + this.threadName = ""; + } + this.messageName = entry.messageName; + this.messageCount = entry.messageCount; + this.recordedMessageCount = entry.recordedMessageCount; + this.exceptionCount = entry.exceptionCount; + this.totalLatencyMicros = entry.totalLatencyMicro; + this.maxLatencyMicros = entry.maxLatencyMicro; + this.cpuUsageMicros = entry.cpuUsageMicro; + this.maxCpuUsageMicros = entry.maxCpuUsageMicro; + } + } +} diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 4ee951aeddaf..0b37d5794741 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -19,8 +19,8 @@ package com.android.internal.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.RectF; import android.graphics.Paint.FontMetricsInt; +import android.graphics.RectF; import android.hardware.input.InputManager; import android.hardware.input.InputManager.InputDeviceListener; import android.os.SystemProperties; @@ -29,12 +29,12 @@ import android.util.Slog; import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.WindowManagerPolicyConstants.PointerEventListener; -import android.view.MotionEvent.PointerCoords; import java.util.ArrayList; @@ -105,10 +105,6 @@ public class PointerLocationView extends View implements InputDeviceListener, } } - private final int ESTIMATE_PAST_POINTS = 4; - private final int ESTIMATE_FUTURE_POINTS = 2; - private final float ESTIMATE_INTERVAL = 0.02f; - private final InputManager mIm; private final ViewConfiguration mVC; @@ -336,37 +332,14 @@ public class PointerLocationView extends View implements InputDeviceListener, } if (drawn) { - // Draw movement estimate curve. - mPaint.setARGB(128, 128, 0, 128); - float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL); - float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL); - for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) { - float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL); - float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL); - canvas.drawLine(lx, ly, x, y, mPaint); - lx = x; - ly = y; - } - // Draw velocity vector. mPaint.setARGB(255, 255, 64, 128); float xVel = ps.mXVelocity * (1000 / 60); float yVel = ps.mYVelocity * (1000 / 60); canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint); - // Draw alternate estimate. + // Draw velocity vector using an alternate VelocityTracker strategy. if (mAltVelocity != null) { - mPaint.setARGB(128, 0, 128, 128); - lx = ps.mAltEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL); - ly = ps.mAltEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL); - for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) { - float x = ps.mAltEstimator.estimateX(i * ESTIMATE_INTERVAL); - float y = ps.mAltEstimator.estimateY(i * ESTIMATE_INTERVAL); - canvas.drawLine(lx, ly, x, y, mPaint); - lx = x; - ly = y; - } - mPaint.setARGB(255, 64, 255, 128); xVel = ps.mAltXVelocity * (1000 / 60); yVel = ps.mAltYVelocity * (1000 / 60); diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp index 3fcedd0264ae..6ebf35c8e1dc 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -22,8 +22,6 @@ #include "SkColorFilter.h" #include "SkColorMatrixFilter.h" -#include <Caches.h> - namespace android { using namespace uirenderer; diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index f8bb77a9650c..755fcfb27141 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -20,7 +20,6 @@ #include "SkMatrix.h" #include "core_jni_helpers.h" -#include <Caches.h> #include <jni.h> namespace android { diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index cff772002b14..68f5bef18de1 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -6,7 +6,6 @@ #include "SkBlendMode.h" #include "core_jni_helpers.h" -#include <Caches.h> #include <jni.h> using namespace android::uirenderer; diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index d098a355085e..3e464c61665f 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -36,6 +36,7 @@ #include "jni.h" #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> +#include "surfacetexture/SurfaceTexture.h" // ---------------------------------------------------------------------------- @@ -80,10 +81,10 @@ static bool isProtectedContext() { // ---------------------------------------------------------------------------- static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz, - const sp<GLConsumer>& surfaceTexture) + const sp<SurfaceTexture>& surfaceTexture) { - GLConsumer* const p = - (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); + SurfaceTexture* const p = + (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); if (surfaceTexture.get()) { surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture); } @@ -108,10 +109,10 @@ static void SurfaceTexture_setProducer(JNIEnv* env, jobject thiz, } static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, - jobject thiz, sp<GLConsumer::FrameAvailableListener> listener) + jobject thiz, sp<SurfaceTexture::FrameAvailableListener> listener) { - GLConsumer::FrameAvailableListener* const p = - (GLConsumer::FrameAvailableListener*) + SurfaceTexture::FrameAvailableListener* const p = + (SurfaceTexture::FrameAvailableListener*) env->GetLongField(thiz, fields.frameAvailableListener); if (listener.get()) { listener->incStrong((void*)SurfaceTexture_setSurfaceTexture); @@ -122,8 +123,8 @@ static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env, env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get()); } -sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { - return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture); +sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) { + return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture); } sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) { @@ -131,7 +132,7 @@ sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) } sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz)); sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL); return surfaceTextureClient; @@ -144,7 +145,7 @@ bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) { // ---------------------------------------------------------------------------- -class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener +class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener { public: JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz); @@ -266,12 +267,12 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, consumer->setMaxBufferCount(1); } - sp<GLConsumer> surfaceTexture; + sp<SurfaceTexture> surfaceTexture; if (isDetached) { - surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, + surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } else { - surfaceTexture = new GLConsumer(consumer, texName, + surfaceTexture = new SurfaceTexture(consumer, texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode); } @@ -306,7 +307,7 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setFrameAvailableListener(0); SurfaceTexture_setFrameAvailableListener(env, thiz, 0); SurfaceTexture_setSurfaceTexture(env, thiz, 0); @@ -315,13 +316,13 @@ static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz) static void SurfaceTexture_setDefaultBufferSize( JNIEnv* env, jobject thiz, jint width, jint height) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->setDefaultBufferSize(width, height); } static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->updateTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to update texture contents (see " @@ -333,7 +334,7 @@ static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz) static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); status_t err = surfaceTexture->releaseTexImage(); if (err == INVALID_OPERATION) { jniThrowException(env, IllegalStateException, "Unable to release texture contents (see " @@ -345,20 +346,20 @@ static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz) static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->detachFromContext(); } static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->attachToContext((GLuint)tex); } static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, jfloatArray jmtx) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); float* mtx = env->GetFloatArrayElements(jmtx, NULL); surfaceTexture->getTransformMatrix(mtx); env->ReleaseFloatArrayElements(jmtx, mtx, 0); @@ -366,19 +367,19 @@ static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz, static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->getTimestamp(); } static void SurfaceTexture_release(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); surfaceTexture->abandon(); } static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz) { - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); + sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz)); return surfaceTexture->isAbandoned(); } diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp index 23c3877e1200..ab8e685d5441 100644 --- a/core/jni/android_hardware_display_DisplayViewport.cpp +++ b/core/jni/android_hardware_display_DisplayViewport.cpp @@ -61,7 +61,7 @@ status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject jstring uniqueId = jstring(env->GetObjectField(viewportObj, gDisplayViewportClassInfo.uniqueId)); if (uniqueId != nullptr) { - viewport->uniqueId.setTo(ScopedUtfChars(env, uniqueId).c_str()); + viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str(); } jobject logicalFrameObj = diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index 494fad7900ef..a698d66965e4 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -37,13 +37,13 @@ static struct { } gInputDeviceClassInfo; jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) { - ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(deviceInfo.getDisplayName().string())); + ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(deviceInfo.getDisplayName().c_str())); if (!nameObj.get()) { return NULL; } ScopedLocalRef<jstring> descriptorObj(env, - env->NewStringUTF(deviceInfo.getIdentifier().descriptor.string())); + env->NewStringUTF(deviceInfo.getIdentifier().descriptor.c_str())); if (!descriptorObj.get()) { return NULL; } diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp index d3a447f1f7dc..1ccb6a8f610c 100644 --- a/core/jni/android_view_TextureLayer.cpp +++ b/core/jni/android_view_TextureLayer.cpp @@ -67,8 +67,7 @@ static void TextureLayer_setTransform(JNIEnv* env, jobject clazz, static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr, jobject surface) { DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); - sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); - layer->setSurfaceTexture(surfaceTexture); + layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface)); } static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz, diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h index c534d4bb9e0a..0ad25876a008 100644 --- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h +++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h @@ -23,14 +23,14 @@ namespace android { -class GLConsumer; class IGraphicBufferProducer; +class SurfaceTexture; extern sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz); extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz); -/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */ -extern sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); +/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */ +extern sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz); /* gets the producer end of the SurfaceTexture */ extern sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5258518db281..f845bd0e3964 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3345,7 +3345,7 @@ @hide @removed --> <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" - android:protectionLevel="signature" /> + android:protectionLevel="signature|privileged" /> <!-- Allows an application to capture secure video output. <p>Not for use by third-party applications.</p> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c8ad31f2ec2c..cf1320c76978 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3352,9 +3352,6 @@ to occur. The expand button will have increased touch boundaries to accomodate this. --> <bool name="config_notificationHeaderClickableForExpand">false</bool> - <!-- Configuration for automotive --> - <bool name="enable_pbap_pce_profile">false</bool> - <!-- Default data warning level in mb --> <integer name="default_data_warning_level_mb">2048</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e20998579b94..48c263e8caef 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3259,8 +3259,6 @@ <java-symbol type="style" name="Theme.DeviceDefault.QuickSettings" /> - <java-symbol type="bool" name="enable_pbap_pce_profile" /> - <java-symbol type="integer" name="default_data_warning_level_mb" /> <java-symbol type="bool" name="config_useVideoPauseWorkaround" /> <java-symbol type="bool" name="config_sendPackageName" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 189a4aa4fe28..517cf4df15a1 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -192,6 +192,7 @@ public class SettingsBackupTest { Settings.Global.DEFAULT_DNS_SERVER, Settings.Global.DEFAULT_INSTALL_LOCATION, Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA, + Settings.Global.DEFAULT_USER_ID_TO_BOOT_INTO, Settings.Global.DESK_DOCK_SOUND, Settings.Global.DESK_UNDOCK_SOUND, Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, @@ -273,6 +274,7 @@ public class SettingsBackupTest { Settings.Global.KEEP_PROFILE_IN_BACKGROUND, Settings.Global.LANG_ID_UPDATE_CONTENT_URL, Settings.Global.LANG_ID_UPDATE_METADATA_URL, + Settings.Global.LAST_ACTIVE_USER_ID, Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java new file mode 100644 index 000000000000..297202bf4d71 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.platform.test.annotations.Presubmit; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Comparator; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +@Presubmit +public final class LooperStatsTest { + private HandlerThread mThreadFirst; + private HandlerThread mThreadSecond; + private Handler mHandlerFirst; + private Handler mHandlerSecond; + private Handler mHandlerAnonymous; + + @Before + public void setUp() { + // The tests are all single-threaded. HandlerThreads are created to allow creating Handlers + // and to test Thread name collection. + mThreadFirst = new HandlerThread("TestThread1"); + mThreadSecond = new HandlerThread("TestThread2"); + mThreadFirst.start(); + mThreadSecond.start(); + + mHandlerFirst = new TestHandlerFirst(mThreadFirst.getLooper()); + mHandlerSecond = new TestHandlerSecond(mThreadSecond.getLooper()); + mHandlerAnonymous = new Handler(mThreadFirst.getLooper()) { + /* To create an anonymous subclass. */ + }; + } + + @After + public void tearDown() { + mThreadFirst.quit(); + mThreadSecond.quit(); + } + + @Test + public void testSingleMessageDispatched() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + + Object token = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(100); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000)); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(1); + LooperStats.ExportedEntry entry = entries.get(0); + assertThat(entry.threadName).isEqualTo("TestThread1"); + assertThat(entry.handlerClassName).isEqualTo( + "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); + assertThat(entry.messageName).isEqualTo("0x3e8" /* 1000 in hex */); + assertThat(entry.messageCount).isEqualTo(1); + assertThat(entry.recordedMessageCount).isEqualTo(1); + assertThat(entry.exceptionCount).isEqualTo(0); + assertThat(entry.totalLatencyMicros).isEqualTo(100); + assertThat(entry.maxLatencyMicros).isEqualTo(100); + assertThat(entry.cpuUsageMicros).isEqualTo(10); + assertThat(entry.maxCpuUsageMicros).isEqualTo(10); + } + + @Test + public void testThrewException() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + + Object token = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(100); + looperStats.tickThreadTime(10); + looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7), + new ArithmeticException()); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(1); + LooperStats.ExportedEntry entry = entries.get(0); + assertThat(entry.threadName).isEqualTo("TestThread1"); + assertThat(entry.handlerClassName).isEqualTo( + "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); + assertThat(entry.messageName).isEqualTo("0x7" /* 7 in hex */); + assertThat(entry.messageCount).isEqualTo(0); + assertThat(entry.recordedMessageCount).isEqualTo(0); + assertThat(entry.exceptionCount).isEqualTo(1); + assertThat(entry.totalLatencyMicros).isEqualTo(0); + assertThat(entry.maxLatencyMicros).isEqualTo(0); + assertThat(entry.cpuUsageMicros).isEqualTo(0); + assertThat(entry.maxCpuUsageMicros).isEqualTo(0); + } + + @Test + public void testMultipleMessagesDispatched() { + TestableLooperStats looperStats = new TestableLooperStats(2, 100); + + // Contributes to entry2. + Object token1 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(100); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000)); + + // Contributes to entry2. + Object token2 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(50); + looperStats.tickThreadTime(20); + looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1000)); + + // Contributes to entry3. + Object token3 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(10); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> { + })); + + // Contributes to entry1. + Object token4 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(100); + looperStats.tickThreadTime(100); + looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1)); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(3); + entries.sort(Comparator.comparing(e -> e.handlerClassName)); + + // Captures data for token4 call. + LooperStats.ExportedEntry entry1 = entries.get(0); + assertThat(entry1.threadName).isEqualTo("TestThread1"); + assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1"); + assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */); + assertThat(entry1.messageCount).isEqualTo(1); + assertThat(entry1.recordedMessageCount).isEqualTo(0); + assertThat(entry1.exceptionCount).isEqualTo(0); + assertThat(entry1.totalLatencyMicros).isEqualTo(0); + assertThat(entry1.maxLatencyMicros).isEqualTo(0); + assertThat(entry1.cpuUsageMicros).isEqualTo(0); + assertThat(entry1.maxCpuUsageMicros).isEqualTo(0); + + // Captures data for token1 and token2 calls. + LooperStats.ExportedEntry entry2 = entries.get(1); + assertThat(entry2.threadName).isEqualTo("TestThread1"); + assertThat(entry2.handlerClassName).isEqualTo( + "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); + assertThat(entry2.messageName).isEqualTo("0x3e8" /* 1000 in hex */); + assertThat(entry2.messageCount).isEqualTo(2); + assertThat(entry2.recordedMessageCount).isEqualTo(1); + assertThat(entry2.exceptionCount).isEqualTo(0); + assertThat(entry2.totalLatencyMicros).isEqualTo(100); + assertThat(entry2.maxLatencyMicros).isEqualTo(100); + assertThat(entry2.cpuUsageMicros).isEqualTo(10); + assertThat(entry2.maxCpuUsageMicros).isEqualTo(10); + + // Captures data for token3 call. + LooperStats.ExportedEntry entry3 = entries.get(2); + assertThat(entry3.threadName).isEqualTo("TestThread2"); + assertThat(entry3.handlerClassName).isEqualTo( + "com.android.internal.os.LooperStatsTest$TestHandlerSecond"); + assertThat(entry3.messageName).startsWith( + "com.android.internal.os.-$$Lambda$LooperStatsTest$"); + assertThat(entry3.messageCount).isEqualTo(1); + assertThat(entry3.recordedMessageCount).isEqualTo(1); + assertThat(entry3.exceptionCount).isEqualTo(0); + assertThat(entry3.totalLatencyMicros).isEqualTo(10); + assertThat(entry3.maxLatencyMicros).isEqualTo(10); + assertThat(entry3.cpuUsageMicros).isEqualTo(10); + assertThat(entry3.maxCpuUsageMicros).isEqualTo(10); + } + + @Test + public void testMessagesOverSizeCap() { + TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */); + + Object token1 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(100); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000)); + + Object token2 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(50); + looperStats.tickThreadTime(20); + looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1001)); + + Object token3 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(10); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1002)); + + Object token4 = looperStats.messageDispatchStarting(); + looperStats.tickRealtime(10); + looperStats.tickThreadTime(10); + looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(1003)); + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + assertThat(entries).hasSize(2); + entries.sort(Comparator.comparing(e -> e.handlerClassName)); + + LooperStats.ExportedEntry entry1 = entries.get(0); + assertThat(entry1.threadName).isEqualTo(""); + assertThat(entry1.handlerClassName).isEqualTo(""); + assertThat(entry1.messageName).isEqualTo("OVERFLOW"); + assertThat(entry1.messageCount).isEqualTo(3); + assertThat(entry1.recordedMessageCount).isEqualTo(1); + assertThat(entry1.exceptionCount).isEqualTo(0); + assertThat(entry1.totalLatencyMicros).isEqualTo(10); + assertThat(entry1.maxLatencyMicros).isEqualTo(10); + assertThat(entry1.cpuUsageMicros).isEqualTo(10); + assertThat(entry1.maxCpuUsageMicros).isEqualTo(10); + + LooperStats.ExportedEntry entry2 = entries.get(1); + assertThat(entry2.threadName).isEqualTo("TestThread1"); + assertThat(entry2.handlerClassName).isEqualTo( + "com.android.internal.os.LooperStatsTest$TestHandlerFirst"); + } + + @Test + public void testInvalidTokensCauseException() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + assertThrows(ClassCastException.class, + () -> looperStats.dispatchingThrewException(new Object(), + mHandlerFirst.obtainMessage(), + new ArithmeticException())); + assertThrows(ClassCastException.class, + () -> looperStats.messageDispatched(new Object(), mHandlerFirst.obtainMessage())); + assertThrows(ClassCastException.class, + () -> looperStats.messageDispatched(123, mHandlerFirst.obtainMessage())); + assertThrows(ClassCastException.class, + () -> looperStats.messageDispatched(mHandlerFirst.obtainMessage(), + mHandlerFirst.obtainMessage())); + + assertThat(looperStats.getEntries()).hasSize(0); + } + + @Test + public void testTracksMultipleHandlerInstancesIfSameClass() { + TestableLooperStats looperStats = new TestableLooperStats(1, 100); + Handler handlerFirstAnother = new TestHandlerFirst(mHandlerFirst.getLooper()); + + Object token1 = looperStats.messageDispatchStarting(); + looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000)); + + Object token2 = looperStats.messageDispatchStarting(); + looperStats.messageDispatched(token2, handlerFirstAnother.obtainMessage(1000)); + + assertThat(looperStats.getEntries()).hasSize(1); + assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2); + } + + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { + try { + r.run(); + Assert.fail("Expected " + exceptionClass + " to be thrown."); + } catch (Exception exception) { + assertThat(exception).isInstanceOf(exceptionClass); + } + } + + private static final class TestableLooperStats extends LooperStats { + private static final long INITIAL_MICROS = 10001000123L; + private int mCount; + private long mRealtimeMicros; + private long mThreadTimeMicros; + private int mSamplingInterval; + + TestableLooperStats(int samplingInterval, int sizeCap) { + super(samplingInterval, sizeCap); + this.mSamplingInterval = samplingInterval; + } + + void tickRealtime(long micros) { + mRealtimeMicros += micros; + } + + void tickThreadTime(long micros) { + mThreadTimeMicros += micros; + } + + @Override + protected long getElapsedRealtimeMicro() { + return INITIAL_MICROS + mRealtimeMicros; + } + + @Override + protected long getThreadTimeMicro() { + return INITIAL_MICROS + mThreadTimeMicros; + } + + @Override + protected boolean shouldCollectDetailedData() { + return mCount++ % mSamplingInterval == 0; + } + } + + private static final class TestHandlerFirst extends Handler { + TestHandlerFirst(Looper looper) { + super(looper); + } + } + + private static final class TestHandlerSecond extends Handler { + TestHandlerSecond(Looper looper) { + super(looper); + } + } +} diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java index 5a78530852c7..c3449dd4fb47 100644 --- a/graphics/java/android/graphics/Insets.java +++ b/graphics/java/android/graphics/Insets.java @@ -16,6 +16,9 @@ package android.graphics; +import android.annotation.NonNull; +import android.annotation.Nullable; + /** * An Insets instance holds four integer offsets which describe changes to the four * edges of a Rectangle. By convention, positive values move edges towards the @@ -24,7 +27,7 @@ package android.graphics; * Insets are immutable so may be treated as values. * */ -public class Insets { +public final class Insets { public static final Insets NONE = new Insets(0, 0, 0, 0); public final int left; @@ -51,7 +54,7 @@ public class Insets { * * @return Insets instance with the appropriate values */ - public static Insets of(int left, int top, int right, int bottom) { + public static @NonNull Insets of(int left, int top, int right, int bottom) { if (left == 0 && top == 0 && right == 0 && bottom == 0) { return NONE; } @@ -65,7 +68,7 @@ public class Insets { * * @return an Insets instance with the appropriate values */ - public static Insets of(Rect r) { + public static @NonNull Insets of(@Nullable Rect r) { return (r == null) ? NONE : of(r.left, r.top, r.right, r.bottom); } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 9dab53680aa9..7fc178738055 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -18,6 +18,7 @@ package android.graphics; import android.annotation.ColorInt; import android.annotation.NonNull; +import android.annotation.Px; import android.annotation.Size; import android.annotation.UnsupportedAppUsage; import android.graphics.fonts.FontVariationAxis; @@ -805,25 +806,39 @@ public class Paint { * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set * * @return true if the underlineText bit is set in the paint's flags. + * @see #getUnderlinePosition() + * @see #getUnderlineThickness() + * @see #setUnderlineText(boolean) */ public final boolean isUnderlineText() { return (getFlags() & UNDERLINE_TEXT_FLAG) != 0; } /** - * Distance from top of the underline to the baseline. Positive values mean below the baseline. - * This method returns where the underline should be drawn independent of if the underlineText - * bit is set at the moment. - * @hide + * Returns the distance from top of the underline to the baseline in pixels. + * + * The result is positive for positions that are below the baseline. + * This method returns where the underline should be drawn independent of if the {@link + * #UNDERLINE_TEXT_FLAG} bit is set. + * + * @return the position of the underline in pixels + * @see #isUnderlineText() + * @see #getUnderlineThickness() + * @see #setUnderlineText(boolean) */ - public float getUnderlinePosition() { + public @Px float getUnderlinePosition() { return nGetUnderlinePosition(mNativePaint); } /** - * @hide + * Returns the thickness of the underline in pixels. + * + * @return the thickness of the underline in pixels + * @see #isUnderlineText() + * @see #getUnderlinePosition() + * @see #setUnderlineText(boolean) */ - public float getUnderlineThickness() { + public @Px float getUnderlineThickness() { return nGetUnderlineThickness(mNativePaint); } @@ -832,6 +847,9 @@ public class Paint { * * @param underlineText true to set the underlineText bit in the paint's * flags, false to clear it. + * @see #isUnderlineText() + * @see #getUnderlinePosition() + * @see #getUnderlineThickness() */ public void setUnderlineText(boolean underlineText) { nSetUnderlineText(mNativePaint, underlineText); @@ -840,26 +858,40 @@ public class Paint { /** * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set * - * @return true if the strikeThruText bit is set in the paint's flags. + * @return true if the {@link #STRIKE_THRU_TEXT_FLAG} bit is set in the paint's flags. + * @see #getStrikeThruPosition() + * @see #getStrikeThruThickness() + * @see #setStrikeThruText(boolean) */ public final boolean isStrikeThruText() { return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0; } /** - * Distance from top of the strike-through line to the baseline. Negative values mean above the - * baseline. This method returns where the strike-through line should be drawn independent of if - * the strikeThruText bit is set at the moment. - * @hide + * Distance from top of the strike-through line to the baseline in pixels. + * + * The result is negative for positions that are above the baseline. + * This method returns where the strike-through line should be drawn independent of if the + * {@link #STRIKE_THRU_TEXT_FLAG} bit is set. + * + * @return the position of the strike-through line in pixels + * @see #getStrikeThruThickness() + * @see #setStrikeThruText(boolean) + * @see #isStrikeThruText() */ - public float getStrikeThruPosition() { + public @Px float getStrikeThruPosition() { return nGetStrikeThruPosition(mNativePaint); } /** - * @hide + * Returns the thickness of the strike-through line in pixels. + * + * @return the position of the strike-through line in pixels + * @see #getStrikeThruPosition() + * @see #setStrikeThruText(boolean) + * @see #isStrikeThruText() */ - public float getStrikeThruThickness() { + public @Px float getStrikeThruThickness() { return nGetStrikeThruThickness(mNativePaint); } @@ -868,6 +900,9 @@ public class Paint { * * @param strikeThruText true to set the strikeThruText bit in the paint's * flags, false to clear it. + * @see #getStrikeThruPosition() + * @see #getStrikeThruThickness() + * @see #isStrikeThruText() */ public void setStrikeThruText(boolean strikeThruText) { nSetStrikeThruText(mNativePaint, strikeThruText); @@ -1560,22 +1595,25 @@ public class Paint { } /** - * Return the paint's word-spacing for text. The default value is 0. + * Return the paint's extra word-spacing for text. * - * @return the paint's word-spacing for drawing text. - * @hide + * The default value is 0. + * + * @return the paint's extra word-spacing for drawing text in pixels. + * @see #setWordSpacing(float) */ public float getWordSpacing() { return nGetWordSpacing(mNativePaint); } /** - * Set the paint's word-spacing for text. The default value is 0. - * The value is in pixels (note the units are not the same as for - * letter-spacing). + * Set the paint's extra word-spacing for text. * - * @param wordSpacing set the paint's word-spacing for drawing text. - * @hide + * Increases the white space width between words with the given amount of pixels. + * The default value is 0. + * + * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels. + * @see #getWordSpacing() */ public void setWordSpacing(float wordSpacing) { nSetWordSpacing(mNativePaint, wordSpacing); diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java index 8de8f813283f..2855227a1002 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -280,7 +280,7 @@ public class StateListDrawable extends DrawableContainer { * @see #getStateDrawable(int) * @see #getStateSet(int) */ - public int getStateDrawableIndex(int[] stateSet) { + public int findStateDrawableIndex(int[] stateSet) { return mStateListState.indexOfStateSet(stateSet); } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 402e390d803f..dc4a0a706bae 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -3205,20 +3205,6 @@ String8 ResTable_config::toString() const { break; } } - if ((colorMode&MASK_HDR) != 0) { - if (res.size() > 0) res.append("-"); - switch (colorMode&MASK_HDR) { - case ResTable_config::HDR_NO: - res.append("lowdr"); - break; - case ResTable_config::HDR_YES: - res.append("highdr"); - break; - default: - res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR)); - break; - } - } if ((colorMode&MASK_WIDE_COLOR_GAMUT) != 0) { if (res.size() > 0) res.append("-"); switch (colorMode&MASK_WIDE_COLOR_GAMUT) { @@ -3233,6 +3219,20 @@ String8 ResTable_config::toString() const { break; } } + if ((colorMode&MASK_HDR) != 0) { + if (res.size() > 0) res.append("-"); + switch (colorMode&MASK_HDR) { + case ResTable_config::HDR_NO: + res.append("lowdr"); + break; + case ResTable_config::HDR_YES: + res.append("highdr"); + break; + default: + res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR)); + break; + } + } if (orientation != ORIENTATION_ANY) { if (res.size() > 0) res.append("-"); switch (orientation) { diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index edce3050255b..2baacbf541dc 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -176,9 +176,7 @@ cc_defaults { "pipeline/skia/SkiaRecordingCanvas.cpp", "pipeline/skia/SkiaVulkanPipeline.cpp", "pipeline/skia/VectorDrawableAtlas.cpp", - "renderstate/PixelBufferState.cpp", "renderstate/RenderState.cpp", - "renderstate/TextureState.cpp", "renderthread/CacheManager.cpp", "renderthread/CanvasContext.cpp", "renderthread/DrawFrameTask.cpp", @@ -190,6 +188,9 @@ cc_defaults { "renderthread/TimeLord.cpp", "renderthread/Frame.cpp", "service/GraphicsStatsService.cpp", + "surfacetexture/EGLConsumer.cpp", + "surfacetexture/ImageConsumer.cpp", + "surfacetexture/SurfaceTexture.cpp", "thread/TaskManager.cpp", "utils/Blur.cpp", "utils/Color.cpp", @@ -201,7 +202,6 @@ cc_defaults { "AnimationContext.cpp", "Animator.cpp", "AnimatorManager.cpp", - "Caches.cpp", "CanvasState.cpp", "CanvasTransform.cpp", "ClipArea.cpp", @@ -210,7 +210,6 @@ cc_defaults { "DeviceInfo.cpp", "FrameInfo.cpp", "FrameInfoVisualizer.cpp", - "GlLayer.cpp", "GpuMemoryTracker.cpp", "HardwareBitmapUploader.cpp", "Interpolator.cpp", @@ -220,7 +219,6 @@ cc_defaults { "Matrix.cpp", "EglReadback.cpp", "PathParser.cpp", - "PixelBuffer.cpp", "ProfileData.cpp", "ProfileDataContainer.cpp", "Properties.cpp", @@ -232,9 +230,7 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", - "Texture.cpp", "VectorDrawable.cpp", - "VkLayer.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp deleted file mode 100644 index 254144448859..000000000000 --- a/libs/hwui/Caches.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Caches.h" - -#include "GlLayer.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include <cutils/properties.h> -#include <utils/Log.h> -#include <utils/String8.h> - -namespace android { -namespace uirenderer { - -Caches* Caches::sInstance = nullptr; - -/////////////////////////////////////////////////////////////////////////////// -// Macros -/////////////////////////////////////////////////////////////////////////////// - -#if DEBUG_CACHE_FLUSH -#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__) -#else -#define FLUSH_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -Caches::Caches(RenderState& renderState) : mInitialized(false) { - INIT_LOGD("Creating OpenGL renderer caches"); - init(); - initStaticProperties(); -} - -bool Caches::init() { - if (mInitialized) return false; - - ATRACE_NAME("Caches::init"); - - mRegionMesh = nullptr; - - mInitialized = true; - - mPixelBufferState = new PixelBufferState(); - mTextureState = new TextureState(); - mTextureState->constructTexture(*this); - - return true; -} - -void Caches::initStaticProperties() { - // OpenGL ES 3.0+ specific features - gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() && - property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true); -} - -void Caches::terminate() { - if (!mInitialized) return; - mRegionMesh.reset(nullptr); - - clearGarbage(); - - delete mPixelBufferState; - mPixelBufferState = nullptr; - delete mTextureState; - mTextureState = nullptr; - mInitialized = false; -} - -/////////////////////////////////////////////////////////////////////////////// -// Memory management -/////////////////////////////////////////////////////////////////////////////// - -void Caches::clearGarbage() {} - -void Caches::flush(FlushMode mode) { - clearGarbage(); - glFinish(); - // Errors during cleanup should be considered non-fatal, dump them and - // and move on. TODO: All errors or just errors like bad surface? - GLUtils::dumpGLErrors(); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h deleted file mode 100644 index 642f9dc50eb1..000000000000 --- a/libs/hwui/Caches.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "DeviceInfo.h" -#include "Extensions.h" -#include "ResourceCache.h" -#include "renderstate/PixelBufferState.h" -#include "renderstate/TextureState.h" -#include "thread/TaskManager.h" -#include "thread/TaskProcessor.h" - -#include <memory> -#include <vector> - -#include <GLES3/gl3.h> - -#include <utils/KeyedVector.h> - -#include <cutils/compiler.h> - -#include <SkPath.h> - -#include <vector> - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Caches -/////////////////////////////////////////////////////////////////////////////// - -class RenderNode; -class RenderState; - -class ANDROID_API Caches { -public: - static Caches& createInstance(RenderState& renderState) { - LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted"); - sInstance = new Caches(renderState); - return *sInstance; - } - - static Caches& getInstance() { - LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created"); - return *sInstance; - } - - static bool hasInstance() { return sInstance != nullptr; } - -private: - explicit Caches(RenderState& renderState); - static Caches* sInstance; - -public: - enum class FlushMode { Layers = 0, Moderate, Full }; - - /** - * Initialize caches. - */ - bool init(); - - bool isInitialized() { return mInitialized; } - - /** - * Flush the cache. - * - * @param mode Indicates how much of the cache should be flushed - */ - void flush(FlushMode mode); - - /** - * Destroys all resources associated with this cache. This should - * be called after a flush(FlushMode::Full). - */ - void terminate(); - - /** - * Call this on each frame to ensure that garbage is deleted from - * GPU memory. - */ - void clearGarbage(); - - /** - * Returns the GL RGBA internal format to use for the current device - * If the device supports linear blending and needSRGB is true, - * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA - */ - constexpr GLint rgbaInternalFormat(bool needSRGB = true) const { - return extensions().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA; - } - -public: - TaskManager tasks; - - bool gpuPixelBuffersEnabled; - - const Extensions& extensions() const { return DeviceInfo::get()->extensions(); } - PixelBufferState& pixelBufferState() { return *mPixelBufferState; } - TextureState& textureState() { return *mTextureState; } - -private: - void initStaticProperties(); - - static void eventMarkNull(GLsizei length, const GLchar* marker) {} - static void startMarkNull(GLsizei length, const GLchar* marker) {} - static void endMarkNull() {} - - // Used to render layers - std::unique_ptr<TextureVertex[]> mRegionMesh; - - bool mInitialized; - - // TODO: move below to RenderState - PixelBufferState* mPixelBufferState = nullptr; - TextureState* mTextureState = nullptr; - -}; // class Caches - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 569de76f294e..00916559a9c2 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -15,27 +15,20 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" -#include "renderthread/EglManager.h" -#include "renderthread/RenderTask.h" #include "utils/PaintUtils.h" namespace android { namespace uirenderer { -DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi) +DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState) : mRenderState(renderState) , mBlend(false) , mSurfaceTexture(nullptr) , mTransform(nullptr) , mGLContextAttached(false) , mUpdateTexImage(false) - , mLayer(nullptr) - , mLayerApi(layerApi) - , mCreateLayerFn(createLayerFn) { + , mLayer(nullptr) { renderState.registerDeferredLayerUpdater(this); } @@ -50,13 +43,9 @@ void DeferredLayerUpdater::destroyLayer() { return; } - if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) { - status_t err = mSurfaceTexture->detachFromContext(); + if (mSurfaceTexture.get() && mGLContextAttached) { + mSurfaceTexture->detachFromView(); mGLContextAttached = false; - if (err != 0) { - // TODO: Elevate to fatal exception - ALOGE("Failed to detach SurfaceTexture from context %d", err); - } } mLayer->postDecStrong(); @@ -75,99 +64,53 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { void DeferredLayerUpdater::apply() { if (!mLayer) { - mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend); + mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode); } mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); if (mSurfaceTexture.get()) { - if (mLayer->getApi() == Layer::Api::Vulkan) { - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateVkTexImage(); - } - } else { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "apply surfaceTexture with non GL backend %x, GL %x, VK %x", - mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan); - if (!mGLContextAttached) { - mGLContextAttached = true; - mUpdateTexImage = true; - mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId()); - } - if (mUpdateTexImage) { - mUpdateTexImage = false; - doUpdateTexImage(); + if (!mGLContextAttached) { + mGLContextAttached = true; + mUpdateTexImage = true; + mSurfaceTexture->attachToView(); + } + if (mUpdateTexImage) { + mUpdateTexImage = false; + sk_sp<SkImage> layerImage; + SkMatrix textureTransform; + android_dataspace dataSpace; + bool queueEmpty = true; + // If the SurfaceTexture queue is in synchronous mode, need to discard all + // but latest frame. Since we can't tell which mode it is in, + // do this unconditionally. + do { + layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty, + mRenderState); + } while (layerImage.get() && (!queueEmpty)); + if (layerImage.get()) { + // force filtration if buffer size != layer size + bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height(); + updateLayer(forceFilter, textureTransform, dataSpace, layerImage); } - GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget(); - static_cast<GlLayer*>(mLayer)->setRenderTarget(renderTarget); } + if (mTransform) { - mLayer->getTransform().load(*mTransform); + mLayer->getTransform() = *mTransform; setTransform(nullptr); } } } -void DeferredLayerUpdater::doUpdateTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL, - "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - if (mSurfaceTexture->updateTexImage() == NO_ERROR) { - float transform[16]; - - int64_t frameNumber = mSurfaceTexture->getFrameNumber(); - // If the GLConsumer queue is in synchronous mode, need to discard all - // but latest frame, using the frame number to tell when we no longer - // have newer frames to target. Since we can't tell which mode it is in, - // do this unconditionally. - int dropCounter = 0; - while (mSurfaceTexture->updateTexImage() == NO_ERROR) { - int64_t newFrameNumber = mSurfaceTexture->getFrameNumber(); - if (newFrameNumber == frameNumber) break; - frameNumber = newFrameNumber; - dropCounter++; - } - - bool forceFilter = false; - sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer(); - if (buffer != nullptr) { - // force filtration if buffer size != layer size - forceFilter = mWidth != static_cast<int>(buffer->getWidth()) || - mHeight != static_cast<int>(buffer->getHeight()); - } - -#if DEBUG_RENDERER - if (dropCounter > 0) { - RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter); - } -#endif - mSurfaceTexture->getTransformMatrix(transform); - - updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace()); - } -} - -void DeferredLayerUpdater::doUpdateVkTexImage() { - LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan, - "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(), - Layer::Api::OpenGL, Layer::Api::Vulkan); - - static const mat4 identityMatrix; - updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN); - - VkLayer* vkLayer = static_cast<VkLayer*>(mLayer); - vkLayer->updateTexture(); -} - -void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform, - android_dataspace dataspace) { +void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp<SkImage>& layerImage) { mLayer->setBlend(mBlend); mLayer->setForceFilter(forceFilter); mLayer->setSize(mWidth, mHeight); - mLayer->getTexTransform().load(textureTransform); + mLayer->getTexTransform() = textureTransform; mLayer->setDataSpace(dataspace); + mLayer->setImage(layerImage); } void DeferredLayerUpdater::detachSurfaceTexture() { diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index fe3ee7a2b4c6..4c323b861002 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -17,18 +17,19 @@ #pragma once #include <SkColorFilter.h> +#include <SkImage.h> #include <SkMatrix.h> #include <cutils/compiler.h> -#include <gui/GLConsumer.h> +#include <map> #include <system/graphics.h> #include <utils/StrongPointer.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include "surfacetexture/SurfaceTexture.h" #include "Layer.h" #include "Rect.h" -#include "renderthread/RenderThread.h" namespace android { namespace uirenderer { @@ -41,12 +42,7 @@ class DeferredLayerUpdater : public VirtualLightRefBase { public: // Note that DeferredLayerUpdater assumes it is taking ownership of the layer // and will not call incrementRef on it as a result. - typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth, - uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha, - SkBlendMode mode, bool blend)> - CreateLayerFn; - ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn, - Layer::Api layerApi); + ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState); ANDROID_API ~DeferredLayerUpdater(); @@ -70,13 +66,13 @@ public: return false; } - ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture) { - if (texture.get() != mSurfaceTexture.get()) { - mSurfaceTexture = texture; + ANDROID_API void setSurfaceTexture(const sp<SurfaceTexture>& consumer) { + if (consumer.get() != mSurfaceTexture.get()) { + mSurfaceTexture = consumer; - GLenum target = texture->getCurrentTextureTarget(); + GLenum target = consumer->getCurrentTextureTarget(); LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES, - "set unsupported GLConsumer with target %x", target); + "set unsupported SurfaceTexture with target %x", target); } } @@ -97,12 +93,11 @@ public: void detachSurfaceTexture(); - void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace); + void updateLayer(bool forceFilter, const SkMatrix& textureTransform, + android_dataspace dataspace, const sk_sp<SkImage>& layerImage); void destroyLayer(); - Layer::Api getBackingLayerApi() { return mLayerApi; } - private: RenderState& mRenderState; @@ -113,17 +108,12 @@ private: sk_sp<SkColorFilter> mColorFilter; int mAlpha = 255; SkBlendMode mMode = SkBlendMode::kSrcOver; - sp<GLConsumer> mSurfaceTexture; + sp<SurfaceTexture> mSurfaceTexture; SkMatrix* mTransform; bool mGLContextAttached; bool mUpdateTexImage; Layer* mLayer; - Layer::Api mLayerApi; - CreateLayerFn mCreateLayerFn; - - void doUpdateTexImage(); - void doUpdateVkTexImage(); }; } /* namespace uirenderer */ diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp deleted file mode 100644 index 432bb8526465..000000000000 --- a/libs/hwui/GlLayer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GlLayer.h" - -#include "Caches.h" -#include "RenderNode.h" -#include "renderstate/RenderState.h" - -namespace android { -namespace uirenderer { - -GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode) - , caches(Caches::getInstance()) - , texture(caches) { - texture.mWidth = layerWidth; - texture.mHeight = layerHeight; - texture.blend = blend; -} - -GlLayer::~GlLayer() { - // There's a rare possibility that Caches could have been destroyed already - // since this method is queued up as a task. - // Since this is a reset method, treat this as non-fatal. - if (caches.isInitialized() && texture.mId) { - texture.deleteTexture(); - } -} - -void GlLayer::onGlContextLost() { - texture.deleteTexture(); -} - -void GlLayer::setRenderTarget(GLenum renderTarget) { - if (renderTarget != getRenderTarget()) { - // new render target: bind with new target, and update filter/wrap - texture.mTarget = renderTarget; - if (texture.mId) { - caches.textureState().bindTexture(texture.target(), texture.mId); - } - texture.setFilter(GL_NEAREST, false, true); - texture.setWrap(GL_CLAMP_TO_EDGE, false, true); - } -} - -void GlLayer::generateTexture() { - if (!texture.mId) { - glGenTextures(1, &texture.mId); - } -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h deleted file mode 100644 index 9f70fdae6790..000000000000 --- a/libs/hwui/GlLayer.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "Layer.h" - -#include "Texture.h" - -namespace android { -namespace uirenderer { - -// Forward declarations -class Caches; - -/** - * A layer has dimensions and is backed by an OpenGL texture or FBO. - */ -class GlLayer : public Layer { -public: - GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend); - virtual ~GlLayer(); - - uint32_t getWidth() const override { return texture.mWidth; } - - uint32_t getHeight() const override { return texture.mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - texture.updateLayout(width, height, texture.internalFormat(), texture.format(), - texture.target()); - } - - void setBlend(bool blend) override { texture.blend = blend; } - - bool isBlend() const override { return texture.blend; } - - inline GLuint getTextureId() const { return texture.id(); } - - inline GLenum getRenderTarget() const { return texture.target(); } - - void setRenderTarget(GLenum renderTarget); - - void generateTexture(); - - /** - * Lost the GL context but the layer is still around, mark it invalid internally - * so the dtor knows not to do any GL work - */ - void onGlContextLost(); - -private: - Caches& caches; - - /** - * The texture backing this layer. - */ - Texture texture; -}; // struct GlLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp index 612bfde1a3fa..a9a7af8f22f3 100644 --- a/libs/hwui/GpuMemoryTracker.cpp +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "Texture.h" #include "utils/StringUtils.h" #include <GpuMemoryTracker.h> @@ -117,22 +116,6 @@ void GpuMemoryTracker::onFrameCompleted() { ATRACE_INT(buf, stats.count); } } - - std::vector<const Texture*> freeList; - for (const auto& obj : gObjectSet) { - if (obj->objectType() == GpuObjectType::Texture) { - const Texture* texture = static_cast<Texture*>(obj); - if (texture->cleanup) { - ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(), - texture->width(), texture->height()); - freeList.push_back(texture); - } - } - } - for (auto& texture : freeList) { - const_cast<Texture*>(texture)->deleteTexture(); - delete texture; - } } } // namespace uirenderer diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index fb8f0337c95e..f59a2e6ee5c1 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -17,17 +17,17 @@ #include "Layer.h" #include "renderstate/RenderState.h" +#include "utils/Color.h" #include <SkToSRGBColorFilter.h> namespace android { namespace uirenderer { -Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha, - SkBlendMode mode) +Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alpha, + SkBlendMode mode) : GpuMemoryTracker(GpuObjectType::Layer) , mRenderState(renderState) - , mApi(api) , mColorFilter(colorFilter) , alpha(alpha) , mode(mode) { @@ -36,6 +36,8 @@ Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter incStrong(nullptr); buildColorSpaceWithFilter(); renderState.registerLayer(this); + texTransform.setIdentity(); + transform.setIdentity(); } Layer::~Layer() { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 31878ac23642..c4e4c1c96ba6 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -23,8 +23,9 @@ #include <SkColorFilter.h> #include <SkColorSpace.h> #include <SkPaint.h> - -#include "Matrix.h" +#include <SkImage.h> +#include <SkMatrix.h> +#include <system/graphics.h> namespace android { namespace uirenderer { @@ -40,24 +41,19 @@ class RenderState; */ class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: - enum class Api { - OpenGL = 0, - Vulkan = 1, - }; - - Api getApi() const { return mApi; } + Layer(RenderState& renderState, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode); ~Layer(); - virtual uint32_t getWidth() const = 0; + virtual uint32_t getWidth() const { return mWidth; } - virtual uint32_t getHeight() const = 0; + virtual uint32_t getHeight() const { return mHeight; } - virtual void setSize(uint32_t width, uint32_t height) = 0; + virtual void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; } - virtual void setBlend(bool blend) = 0; + virtual void setBlend(bool blend) { mBlend = blend; } - virtual bool isBlend() const = 0; + virtual bool isBlend() const { return mBlend; } inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; } @@ -84,9 +80,9 @@ public: inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; } - inline mat4& getTexTransform() { return texTransform; } + inline SkMatrix& getTexTransform() { return texTransform; } - inline mat4& getTransform() { return transform; } + inline SkMatrix& getTransform() { return transform; } /** * Posts a decStrong call to the appropriate thread. @@ -94,16 +90,17 @@ public: */ void postDecStrong(); + inline void setImage(const sk_sp<SkImage>& image) { this->layerImage = image; } + + inline sk_sp<SkImage> getImage() const { return this->layerImage; } + protected: - Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode); RenderState& mRenderState; private: void buildColorSpaceWithFilter(); - Api mApi; - /** * Color filter used to draw this layer. Optional. */ @@ -137,12 +134,32 @@ private: /** * Optional texture coordinates transform. */ - mat4 texTransform; + SkMatrix texTransform; /** * Optional transform. */ - mat4 transform; + SkMatrix transform; + + /** + * An image backing the layer. + */ + sk_sp<SkImage> layerImage; + + /** + * layer width. + */ + uint32_t mWidth = 0; + + /** + * layer height. + */ + uint32_t mHeight = 0; + + /** + * enable blending + */ + bool mBlend = false; }; // struct Layer diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp deleted file mode 100644 index 910a9889db1f..000000000000 --- a/libs/hwui/PixelBuffer.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PixelBuffer.h" - -#include "Debug.h" -#include "Extensions.h" -#include "Properties.h" -#include "renderstate/RenderState.h" -#include "utils/GLUtils.h" - -#include <utils/Log.h> - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// CPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class CpuPixelBuffer : public PixelBuffer { -public: - CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - std::unique_ptr<uint8_t[]> mBuffer; -}; - -CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mBuffer(new uint8_t[width * height * formatSize(format)]) {} - -uint8_t* CpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mAccessMode = mode; - } - return mBuffer.get(); -} - -void CpuPixelBuffer::unmap() { - mAccessMode = kAccessMode_None; -} - -void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - &mBuffer[offset]); -} - -/////////////////////////////////////////////////////////////////////////////// -// GPU pixel buffer -/////////////////////////////////////////////////////////////////////////////// - -class GpuPixelBuffer : public PixelBuffer { -public: - GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height); - ~GpuPixelBuffer(); - - uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override; - - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override; - -protected: - void unmap() override; - -private: - GLuint mBuffer; - uint8_t* mMappedPointer; - Caches& mCaches; -}; - -GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height) - : PixelBuffer(format, width, height) - , mMappedPointer(nullptr) - , mCaches(Caches::getInstance()) { - glGenBuffers(1, &mBuffer); - - mCaches.pixelBufferState().bind(mBuffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW); - mCaches.pixelBufferState().unbind(); -} - -GpuPixelBuffer::~GpuPixelBuffer() { - glDeleteBuffers(1, &mBuffer); -} - -uint8_t* GpuPixelBuffer::map(AccessMode mode) { - if (mAccessMode == kAccessMode_None) { - mCaches.pixelBufferState().bind(mBuffer); - mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); - if (CC_UNLIKELY(!mMappedPointer)) { - GLUtils::dumpGLErrors(); - LOG_ALWAYS_FATAL("Failed to map PBO"); - } - mAccessMode = mode; - mCaches.pixelBufferState().unbind(); - } - - return mMappedPointer; -} - -void GpuPixelBuffer::unmap() { - if (mAccessMode != kAccessMode_None) { - if (mMappedPointer) { - mCaches.pixelBufferState().bind(mBuffer); - GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - if (status == GL_FALSE) { - ALOGE("Corrupted GPU pixel buffer"); - } - } - mAccessMode = kAccessMode_None; - mMappedPointer = nullptr; - } -} - -void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) { - // If the buffer is not mapped, unmap() will not bind it - mCaches.pixelBufferState().bind(mBuffer); - unmap(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE, - reinterpret_cast<void*>(offset)); - mCaches.pixelBufferState().unbind(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Factory -/////////////////////////////////////////////////////////////////////////////// - -PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) { - if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) { - return new GpuPixelBuffer(format, width, height); - } - return new CpuPixelBuffer(format, width, height); -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h deleted file mode 100644 index e7e341b90ad3..000000000000 --- a/libs/hwui/PixelBuffer.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_PIXEL_BUFFER_H -#define ANDROID_HWUI_PIXEL_BUFFER_H - -#include <GLES3/gl3.h> - -#include <log/log.h> - -namespace android { -namespace uirenderer { - -/** - * Represents a pixel buffer. A pixel buffer will be backed either by a - * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other - * versions. If the buffer is backed by a PBO it will of type - * GL_PIXEL_UNPACK_BUFFER. - * - * To read from or write into a PixelBuffer you must first map the - * buffer using the map(AccessMode) method. This method returns a - * pointer to the beginning of the buffer. - * - * Before the buffer can be used by the GPU, for instance to upload - * a texture, you must first unmap the buffer. To do so, call the - * unmap() method. - * - * Mapping and unmapping a PixelBuffer can have the side effect of - * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is - * therefore recommended to call Caches::unbindPixelbuffer() after - * using a PixelBuffer to upload to a texture. - */ -class PixelBuffer { -public: - enum BufferType { kBufferType_Auto, kBufferType_CPU }; - - enum AccessMode { - kAccessMode_None = 0, - kAccessMode_Read = GL_MAP_READ_BIT, - kAccessMode_Write = GL_MAP_WRITE_BIT, - kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT - }; - - /** - * Creates a new PixelBuffer object with the specified format and - * dimensions. The buffer is immediately allocated. - * - * The buffer type specifies how the buffer should be allocated. - * By default this method will automatically choose whether to allocate - * a CPU or GPU buffer. - */ - static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height, - BufferType type = kBufferType_Auto); - - virtual ~PixelBuffer() {} - - /** - * Returns the format of this render buffer. - */ - GLenum getFormat() const { return mFormat; } - - /** - * Maps this before with the specified access mode. This method - * returns a pointer to the region of memory where the buffer was - * mapped. - * - * If the buffer is already mapped when this method is invoked, - * this method will return the previously mapped pointer. The - * access mode can only be changed by calling unmap() first. - * - * The specified access mode cannot be kAccessMode_None. - */ - virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0; - - /** - * Returns the current access mode for this buffer. If the buffer - * is not mapped, this method returns kAccessMode_None. - */ - AccessMode getAccessMode() const { return mAccessMode; } - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - */ - virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0; - - /** - * Upload the specified rectangle of this pixel buffer as a - * GL_TEXTURE_2D texture. Calling this method will trigger - * an unmap() if necessary. - * - * This is a convenience function provided to save callers the - * trouble of computing the offset parameter. - */ - void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - upload(x, y, width, height, getOffset(x, y)); - } - - /** - * Returns the width of the render buffer in pixels. - */ - uint32_t getWidth() const { return mWidth; } - - /** - * Returns the height of the render buffer in pixels. - */ - uint32_t getHeight() const { return mHeight; } - - /** - * Returns the size of this pixel buffer in bytes. - */ - uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); } - - /** - * Returns the offset of a pixel in this pixel buffer, in bytes. - */ - uint32_t getOffset(uint32_t x, uint32_t y) const { - return (y * mWidth + x) * formatSize(mFormat); - } - - /** - * Returns the number of bytes per pixel in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatSize(GLenum format) { - switch (format) { - case GL_ALPHA: - return 1; - case GL_RGBA: - return 4; - } - return 0; - } - - /** - * Returns the alpha channel offset in the specified format. - * - * Supported formats: - * GL_ALPHA - * GL_RGBA - */ - static uint32_t formatAlphaOffset(GLenum format) { - switch (format) { - case GL_ALPHA: - return 0; - case GL_RGBA: - return 3; - } - - ALOGE("unsupported format: %d", format); - return 0; - } - -protected: - /** - * Creates a new render buffer in the specified format and dimensions. - * The format must be GL_ALPHA or GL_RGBA. - */ - PixelBuffer(GLenum format, uint32_t width, uint32_t height) - : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {} - - /** - * Unmaps this buffer, if needed. After the buffer is unmapped, - * the pointer previously returned by map() becomes invalid and - * should not be used. - */ - virtual void unmap() = 0; - - GLenum mFormat; - - uint32_t mWidth; - uint32_t mHeight; - - AccessMode mAccessMode; - -}; // class PixelBuffer - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_PIXEL_BUFFER_H diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 0766e3b7ed28..7966845ff814 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -16,7 +16,6 @@ #pragma once -#include "Caches.h" #include "DeviceInfo.h" #include "Outline.h" #include "Rect.h" diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 464a58d0c0f8..65bee476f14d 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -15,7 +15,6 @@ */ #include "ResourceCache.h" -#include "Caches.h" namespace android { @@ -112,13 +111,9 @@ void ResourceCache::destructorLocked(Res_png_9patch* resource) { ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr; if (ref == nullptr) { // If we're not tracking this resource, just delete it - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - delete[](int8_t*) resource; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + delete[](int8_t*) resource; return; } ref->destroyed = true; @@ -135,14 +130,10 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource if (ref->destroyed) { switch (ref->resourceType) { case kNinePatch: { - if (Caches::hasInstance()) { - // DEAD CODE - } else { - // A Res_png_9patch is actually an array of byte that's larger - // than sizeof(Res_png_9patch). It must be freed as an array. - int8_t* patch = (int8_t*)resource; - delete[] patch; - } + // A Res_png_9patch is actually an array of byte that's larger + // than sizeof(Res_png_9patch). It must be freed as an array. + int8_t* patch = (int8_t*)resource; + delete[] patch; } break; } } diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp deleted file mode 100644 index 1e90eebe3bb8..000000000000 --- a/libs/hwui/Texture.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Texture.h" -#include "Caches.h" -#include "utils/GLUtils.h" -#include "utils/MathUtils.h" -#include "utils/TraceUtils.h" - -#include <utils/Log.h> - -#include <math/mat4.h> - -#include <SkCanvas.h> - -namespace android { -namespace uirenderer { - -// Number of bytes used by a texture in the given format -static int bytesPerPixel(GLint glFormat) { - switch (glFormat) { - // The wrapped-texture case, usually means a SurfaceTexture - case 0: - return 0; - case GL_LUMINANCE: - case GL_ALPHA: - return 1; - case GL_SRGB8: - case GL_RGB: - return 3; - case GL_SRGB8_ALPHA8: - case GL_RGBA: - return 4; - case GL_RGBA16F: - return 8; - default: - LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat); - } -} - -void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) { - if (force || wrapS != mWrapS || wrapT != mWrapT) { - mWrapS = wrapS; - mWrapT = wrapT; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS); - glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT); - } -} - -void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) { - if (force || min != mMinFilter || mag != mMagFilter) { - mMinFilter = min; - mMagFilter = mag; - - if (bindTexture) { - mCaches.textureState().bindTexture(mTarget, mId); - } - - if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; - - glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min); - glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag); - } -} - -void Texture::deleteTexture() { - mCaches.textureState().deleteTexture(mId); - mId = 0; - mTarget = GL_NONE; - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } -} - -bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - if (mWidth == width && mHeight == height && mFormat == format && - mInternalFormat == internalFormat && mTarget == target) { - return false; - } - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat)); - return true; -} - -void Texture::resetCachedParams() { - mWrapS = GL_REPEAT; - mWrapT = GL_REPEAT; - mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - mMagFilter = GL_LINEAR; -} - -void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, - GLenum type, const void* pixels) { - GL_CHECKPOINT(MODERATE); - - // We don't have color space information, we assume the data is gamma encoded - mIsLinear = false; - - bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D); - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - resetCachedParams(); - } - mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); - if (needsAlloc) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } else if (pixels) { - glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels); - } - GL_CHECKPOINT(MODERATE); -} - -void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) { - EGLDisplay eglDisplayHandle = eglGetCurrentDisplay(); - if (mEglImageHandle != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle); - mEglImageHandle = EGL_NO_IMAGE_KHR; - } - mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - buffer->getNativeBuffer(), 0); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle); -} - -static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type, - GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, - const GLvoid* data) { - const bool useStride = - stride != width && Caches::getInstance().extensions().hasUnpackRowLength(); - if ((stride == width) || useStride) { - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } - - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); - } - - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - } else { - // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width - - GLvoid* temp = (GLvoid*)malloc(width * height * bpp); - if (!temp) return; - - uint8_t* pDst = (uint8_t*)temp; - uint8_t* pSrc = (uint8_t*)data; - for (GLsizei i = 0; i < height; i++) { - memcpy(pDst, pSrc, width * bpp); - pDst += width * bpp; - pSrc += stride * bpp; - } - - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); - } - - free(temp); - } -} - -void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, bool needSRGB, - GLint* outInternalFormat, GLint* outFormat, - GLint* outType) { - switch (colorType) { - case kAlpha_8_SkColorType: - *outFormat = GL_ALPHA; - *outInternalFormat = GL_ALPHA; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGB_565_SkColorType: - if (needSRGB) { - // We would ideally use a GL_RGB/GL_SRGB8 texture but the - // intermediate Skia bitmap needs to be ARGB_8888 - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(); - *outType = GL_UNSIGNED_BYTE; - } else { - *outFormat = GL_RGB; - *outInternalFormat = GL_RGB; - *outType = GL_UNSIGNED_SHORT_5_6_5; - } - break; - // ARGB_4444 is upconverted to RGBA_8888 - case kARGB_4444_SkColorType: - case kN32_SkColorType: - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(needSRGB); - *outType = GL_UNSIGNED_BYTE; - break; - case kGray_8_SkColorType: - *outFormat = GL_LUMINANCE; - *outInternalFormat = GL_LUMINANCE; - *outType = GL_UNSIGNED_BYTE; - break; - case kRGBA_F16_SkColorType: - if (caches.extensions().getMajorGlVersion() >= 3) { - // This format is always linear - *outFormat = GL_RGBA; - *outInternalFormat = GL_RGBA16F; - *outType = GL_HALF_FLOAT; - } else { - *outFormat = GL_RGBA; - *outInternalFormat = caches.rgbaInternalFormat(true); - *outType = GL_UNSIGNED_BYTE; - } - break; - default: - LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); - break; - } -} - -SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp<SkColorSpace> sRGB) { - SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(), - bitmap.info().alphaType(), - hasLinearBlending ? sRGB : nullptr)); - rgbaBitmap.eraseColor(0); - - if (bitmap.colorType() == kRGBA_F16_SkColorType) { - // Drawing RGBA_F16 onto ARGB_8888 is not supported - bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()), - rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0); - } else { - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); - } - - return rgbaBitmap; -} - -bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) { - return info.colorType() == kARGB_4444_SkColorType || - (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && - info.colorSpace()->isSRGB()) || - (info.colorType() == kRGBA_F16_SkColorType && - Caches::getInstance().extensions().getMajorGlVersion() < 3); -} - -void Texture::upload(Bitmap& bitmap) { - ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); - - // We could also enable mipmapping if both bitmap dimensions are powers - // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = mCaches.extensions().hasNPot(); - - // If the texture had mipmap enabled but not anymore, - // force a glTexImage2D to discard the mipmap levels - bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); - bool setDefaultParams = false; - - if (!mId) { - glGenTextures(1, &mId); - needsAlloc = true; - setDefaultParams = true; - } - - bool hasLinearBlending = mCaches.extensions().hasLinearBlending(); - bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace()); - - GLint internalFormat, format, type; - colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending, - &internalFormat, &format, &type); - - // Some devices don't support GL_RGBA16F, so we need to compare the color type - // and internal GL format to decide what to do with 16 bit bitmaps - bool rgba16fNeedsConversion = - bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F; - - // RGBA16F is always linear extended sRGB - if (internalFormat == GL_RGBA16F) { - mIsLinear = true; - } - - mConnector.reset(); - - // Alpha masks don't have color profiles - // If an RGBA16F bitmap needs conversion, we know the target will be sRGB - if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) { - SkColorSpace* colorSpace = bitmap.info().colorSpace(); - // If the bitmap is sRGB we don't need conversion - if (colorSpace != nullptr && !colorSpace->isSRGB()) { - SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor); - if (!colorSpace->toXYZD50(&xyzMatrix)) { - ALOGW("Incompatible color space!"); - } else { - SkColorSpaceTransferFn fn; - if (!colorSpace->isNumericalTransferFn(&fn)) { - ALOGW("Incompatible color space, no numerical transfer function!"); - } else { - float data[16]; - xyzMatrix.asColMajorf(data); - - ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC, - fn.fD, fn.fE, fn.fF}; - ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p); - mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB())); - - // A non-sRGB color space might have a transfer function close enough to sRGB - // that we can save shader instructions by using an sRGB sampler - // This is only possible if we have hardware support for sRGB textures - if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() && - !bitmap.isHardware()) { - internalFormat = GL_SRGB8_ALPHA8; - } - } - } - } - } - - GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; - needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target); - - blend = !bitmap.isOpaque(); - mCaches.textureState().bindTexture(mTarget, mId); - - // TODO: Handle sRGB gray bitmaps - if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) { - SkBitmap skBitmap; - bitmap.getSkBitmap(&skBitmap); - sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB(); - SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB)); - uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(), - rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(), - rgbaBitmap.getPixels()); - } else if (bitmap.isHardware()) { - uploadHardwareBitmapToTexture(bitmap.graphicBuffer()); - } else { - uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(), - bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), - bitmap.pixels()); - } - - if (canMipMap) { - mipMap = bitmap.hasHardwareMipMap(); - if (mipMap) { - glGenerateMipmap(GL_TEXTURE_2D); - } - } - - if (setDefaultParams) { - setFilter(GL_NEAREST); - setWrap(GL_CLAMP_TO_EDGE); - } -} - -void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target) { - mId = id; - mWidth = width; - mHeight = height; - mFormat = format; - mInternalFormat = internalFormat; - mTarget = target; - mConnector.reset(); - // We're wrapping an existing texture, so don't double count this memory - notifySizeChanged(0); -} - -TransferFunctionType Texture::getTransferFunctionType() const { - if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) { - const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters(); - if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) { - if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) && - MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) { - if (MathUtils::areEqual(p.g, 1.0f)) { - return TransferFunctionType::None; - } - return TransferFunctionType::Gamma; - } - return TransferFunctionType::Limited; - } - return TransferFunctionType::Full; - } - return TransferFunctionType::None; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h deleted file mode 100644 index 5b7e4e261f30..000000000000 --- a/libs/hwui/Texture.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_HWUI_TEXTURE_H -#define ANDROID_HWUI_TEXTURE_H - -#include "GpuMemoryTracker.h" -#include "hwui/Bitmap.h" -#include "utils/Color.h" - -#include <memory> - -#include <math/mat3.h> - -#include <ui/ColorSpace.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES3/gl3.h> -#include <SkBitmap.h> - -namespace android { - -class GraphicBuffer; - -namespace uirenderer { - -class Caches; -class UvMapper; -class Layer; - -/** - * Represents an OpenGL texture. - */ -class Texture : public GpuMemoryTracker { -public: - static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending, - sk_sp<SkColorSpace> sRGB); - static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending); - static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, - bool needSRGB, GLint* outInternalFormat, - GLint* outFormat, GLint* outType); - - explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {} - - virtual ~Texture() {} - - inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { - setWrapST(wrap, wrap, bindTexture, force); - } - - virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, - bool force = false); - - inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { - setFilterMinMag(filter, filter, bindTexture, force); - } - - virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, - bool force = false); - - /** - * Convenience method to call glDeleteTextures() on this texture's id. - */ - void deleteTexture(); - - /** - * Sets the width, height, and format of the texture along with allocating - * the texture ID. Does nothing if the width, height, and format are already - * the requested values. - * - * The image data is undefined after calling this. - */ - void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) { - upload(internalFormat, width, height, format, - internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr); - } - - /** - * Updates this Texture with the contents of the provided Bitmap, - * also setting the appropriate width, height, and format. It is not necessary - * to call resize() prior to this. - * - * Note this does not set the generation from the Bitmap. - */ - void upload(Bitmap& source); - - /** - * Basically glTexImage2D/glTexSubImage2D. - */ - void upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type, - const void* pixels); - - /** - * Wraps an existing texture. - */ - void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target); - - GLuint id() const { return mId; } - - uint32_t width() const { return mWidth; } - - uint32_t height() const { return mHeight; } - - GLint format() const { return mFormat; } - - GLint internalFormat() const { return mInternalFormat; } - - GLenum target() const { return mTarget; } - - /** - * Returns nullptr if this texture does not require color space conversion - * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion - * is required. - */ - constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); } - - constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; } - - TransferFunctionType getTransferFunctionType() const; - - /** - * Returns true if this texture uses a linear encoding format. - */ - constexpr bool isLinear() const { return mIsLinear; } - - /** - * Generation of the backing bitmap, - */ - uint32_t generation = 0; - /** - * Indicates whether the texture requires blending. - */ - bool blend = false; - /** - * Indicates whether this texture should be cleaned up after use. - */ - bool cleanup = false; - /** - * Optional, size of the original bitmap. - */ - uint32_t bitmapSize = 0; - /** - * Indicates whether this texture will use trilinear filtering. - */ - bool mipMap = false; - - /** - * Optional, pointer to a texture coordinates mapper. - */ - const UvMapper* uvMapper = nullptr; - - /** - * Whether or not the Texture is marked in use and thus not evictable for - * the current frame. This is reset at the start of a new frame. - */ - void* isInUse = nullptr; - -private: - // TODO: Temporarily grant private access to GlLayer, remove once - // GlLayer can be de-tangled from being a dual-purpose render target - // and external texture wrapper - friend class GlLayer; - - // Returns true if the texture layout (size, format, etc.) changed, false if it was the same - bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format, - GLenum target); - void uploadHardwareBitmapToTexture(GraphicBuffer* buffer); - void resetCachedParams(); - - GLuint mId = 0; - uint32_t mWidth = 0; - uint32_t mHeight = 0; - GLint mFormat = 0; - GLint mInternalFormat = 0; - GLenum mTarget = GL_NONE; - EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR; - - /* See GLES spec section 3.8.14 - * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is - * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR. - * s, t, and r wrap modes are all set to REPEAT." - */ - GLenum mWrapS = GL_REPEAT; - GLenum mWrapT = GL_REPEAT; - GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR; - GLenum mMagFilter = GL_LINEAR; - - // Indicates whether the content of the texture is in linear space - bool mIsLinear = false; - - Caches& mCaches; - - std::unique_ptr<ColorSpaceConnector> mConnector; -}; // struct Texture - -class AutoTexture { -public: - explicit AutoTexture(Texture* texture) : texture(texture) {} - ~AutoTexture() { - if (texture && texture->cleanup) { - texture->deleteTexture(); - delete texture; - } - } - - Texture* const texture; -}; // class AutoTexture - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp deleted file mode 100644 index 30fba7ae7d9b..000000000000 --- a/libs/hwui/VkLayer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "VkLayer.h" - -#include "renderstate/RenderState.h" - -#include <SkCanvas.h> -#include <SkSurface.h> - -namespace android { -namespace uirenderer { - -void VkLayer::updateTexture() { - sk_sp<SkSurface> surface; - SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType); - surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info); - surface->getCanvas()->clear(SK_ColorBLUE); - mImage = surface->makeImageSnapshot(); -} - -void VkLayer::onVkContextDestroyed() { - mImage = nullptr; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h deleted file mode 100644 index e9664d04b7a5..000000000000 --- a/libs/hwui/VkLayer.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "Layer.h" - -#include <SkImage.h> - -namespace android { -namespace uirenderer { -/** - * A layer has dimensions and is backed by a VkImage. - */ -class VkLayer : public Layer { -public: - VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) - : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode) - , mWidth(layerWidth) - , mHeight(layerHeight) - , mBlend(blend) {} - - virtual ~VkLayer() {} - - uint32_t getWidth() const override { return mWidth; } - - uint32_t getHeight() const override { return mHeight; } - - void setSize(uint32_t width, uint32_t height) override { - mWidth = width; - mHeight = height; - } - - void setBlend(bool blend) override { mBlend = blend; } - - bool isBlend() const override { return mBlend; } - - sk_sp<SkImage> getImage() { return mImage; } - - void updateTexture(); - - // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to - // destroy any VkImages that were made with that context. - void onVkContextDestroyed(); - -private: - int mWidth; - int mHeight; - bool mBlend; - - sk_sp<SkImage> mImage; - -}; // struct VkLayer - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index a7d37f8aa45c..3939696692d2 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -15,11 +15,11 @@ */ #include "Bitmap.h" -#include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" +#include <utils/Trace.h> #include <sys/mman.h> diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index d9856336e8bd..e8332a807636 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -178,8 +178,8 @@ void Typeface::setRobotoTypefaceForTest() { struct stat st = {}; LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont); void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size)); - sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release()); + std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size)); + sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData)); LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont); std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>( diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index c41f6a6f0ee6..b6cd4b08e5fe 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -15,8 +15,6 @@ */ #include "LayerDrawable.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "GrBackendSurface.h" #include "SkColorFilter.h" @@ -41,42 +39,21 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer return false; } // transform the matrix based on the layer - SkMatrix layerTransform; - layer->getTransform().copyTo(layerTransform); - sk_sp<SkImage> layerImage; + SkMatrix layerTransform = layer->getTransform(); + sk_sp<SkImage> layerImage = layer->getImage(); const int layerWidth = layer->getWidth(); const int layerHeight = layer->getHeight(); - if (layer->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast<GlLayer*>(layer); - GrGLTextureInfo externalTexture; - externalTexture.fTarget = glLayer->getRenderTarget(); - externalTexture.fID = glLayer->getTextureId(); - // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't - // expose that info we use it as our default. Further, given that we only use this texture - // as a source this will not impact how Skia uses the texture. The only potential affect - // this is anticipated to have is that for some format types if we are not bound as an OES - // texture we may get invalid results for SKP capture if we read back the texture. - externalTexture.fFormat = GL_RGBA8; - GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture); - layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin, - kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); - } else { - SkASSERT(layer->getApi() == Layer::Api::Vulkan); - VkLayer* vkLayer = static_cast<VkLayer*>(layer); - canvas->clear(SK_ColorGREEN); - layerImage = vkLayer->getImage(); - } if (layerImage) { SkMatrix textureMatrixInv; - layer->getTexTransform().copyTo(textureMatrixInv); + textureMatrixInv = layer->getTexTransform(); // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed // use bottom left origin and remove flipV and invert transformations. SkMatrix flipV; flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1); textureMatrixInv.preConcat(flipV); textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight); - textureMatrixInv.postScale(layerWidth, layerHeight); + textureMatrixInv.postScale(layerImage->width(), layerImage->height()); SkMatrix textureMatrix; if (!textureMatrixInv.invert(&textureMatrix)) { textureMatrix = textureMatrixInv; @@ -95,6 +72,9 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer paint.setAlpha(layer->getAlpha()); paint.setBlendMode(layer->getMode()); paint.setColorFilter(layer->getColorSpaceWithFilter()); + if (layer->getForceFilter()) { + paint.setFilterQuality(kLow_SkFilterQuality); + } const bool nonIdentityMatrix = !matrix.isIdentity(); if (nonIdentityMatrix) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 78f5a71dee3b..2ae37233098e 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,7 +17,6 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "LayerDrawable.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" @@ -187,18 +186,9 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, - bool blend) { - GlLayer* layer = - new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); - layer->generateTexture(); - return layer; -} - DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { mRenderThread.requireGlContext(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaOpenGLPipeline::onStop() { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index b2519fe59891..5f2eee4523fc 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -20,7 +20,6 @@ #include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" -#include "VkLayer.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" @@ -114,16 +113,10 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi return false; } -static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight, - sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, - bool blend) { - return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend); -} - DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { mVkManager.initialize(); - return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan); + return new DeferredLayerUpdater(mRenderThread.renderState()); } void SkiaVulkanPipeline::onStop() {} diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp deleted file mode 100644 index 3a6efb833c47..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "renderstate/PixelBufferState.h" - -namespace android { -namespace uirenderer { - -PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {} - -bool PixelBufferState::bind(GLuint buffer) { - if (mCurrentPixelBuffer != buffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); - mCurrentPixelBuffer = buffer; - return true; - } - return false; -} - -bool PixelBufferState::unbind() { - if (mCurrentPixelBuffer) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - mCurrentPixelBuffer = 0; - return true; - } - return false; -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h deleted file mode 100644 index f7ae6c575f6a..000000000000 --- a/libs/hwui/renderstate/PixelBufferState.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef RENDERSTATE_PIXELBUFFERSTATE_H -#define RENDERSTATE_PIXELBUFFERSTATE_H - -#include <GLES3/gl3.h> - -namespace android { -namespace uirenderer { - -class PixelBufferState { - friend class Caches; // TODO: move to RenderState -public: - bool bind(GLuint buffer); - bool unbind(); - -private: - PixelBufferState(); - GLuint mCurrentPixelBuffer; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 3be84f588a20..b524bcb096da 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -16,8 +16,6 @@ #include "renderstate/RenderState.h" #include <GpuMemoryTracker.h> #include "DeferredLayerUpdater.h" -#include "GlLayer.h" -#include "VkLayer.h" #include "Snapshot.h" #include "renderthread/CanvasContext.h" @@ -39,44 +37,11 @@ RenderState::RenderState(renderthread::RenderThread& thread) RenderState::~RenderState() { } -void RenderState::onGLContextCreated() { +void RenderState::onContextCreated() { GpuMemoryTracker::onGpuContextCreated(); - - // This is delayed because the first access of Caches makes GL calls - if (!mCaches) { - mCaches = &Caches::createInstance(*this); - } - mCaches->init(); } -static void layerLostGlContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL, - "layerLostGlContext on non GL layer"); - static_cast<GlLayer*>(layer)->onGlContextLost(); -} - -void RenderState::onGLContextDestroyed() { - // TODO: reset all cached state in state objects - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); - - mCaches->terminate(); - - destroyLayersInUpdater(); - GpuMemoryTracker::onGpuContextDestroyed(); -} - -void RenderState::onVkContextCreated() { - GpuMemoryTracker::onGpuContextCreated(); -} - -static void layerDestroyedVkContext(Layer* layer) { - LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan, - "layerLostVkContext on non Vulkan layer"); - static_cast<VkLayer*>(layer)->onVkContextDestroyed(); -} - -void RenderState::onVkContextDestroyed() { - std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext); +void RenderState::onContextDestroyed() { destroyLayersInUpdater(); GpuMemoryTracker::onGpuContextDestroyed(); } @@ -85,10 +50,6 @@ GrContext* RenderState::getGrContext() const { return mRenderThread.getGrContext(); } -void RenderState::flush(Caches::FlushMode mode) { - if (mCaches) mCaches->flush(mode); -} - void RenderState::onBitmapDestroyed(uint32_t pixelRefId) { // DEAD CODE } @@ -126,42 +87,6 @@ void RenderState::deleteFramebuffer(GLuint fbo) { glDeleteFramebuffers(1, &fbo); } -void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { - if (mode == DrawGlInfo::kModeProcessNoContext) { - // If there's no context we don't need to interrupt as there's - // no gl state to save/restore - (*functor)(mode, info); - } else { - interruptForFunctorInvoke(); - (*functor)(mode, info); - resumeFromFunctorInvoke(); - } -} - -void RenderState::interruptForFunctorInvoke() { - mCaches->textureState().resetActiveTexture(); - debugOverdraw(false, false); - // TODO: We need a way to know whether the functor is sRGB aware (b/32072673) - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glDisable(GL_FRAMEBUFFER_SRGB_EXT); - } -} - -void RenderState::resumeFromFunctorInvoke() { - if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) { - glEnable(GL_FRAMEBUFFER_SRGB_EXT); - } - - glViewport(0, 0, mViewportWidth, mViewportHeight); - glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); - debugOverdraw(false, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches->textureState().activateTexture(0); - mCaches->textureState().resetBoundTextures(); -} - void RenderState::debugOverdraw(bool enable, bool clear) { // DEAD CODE } @@ -190,5 +115,9 @@ void RenderState::dump() { // DEAD CODE } +renderthread::RenderThread& RenderState::getRenderThread() { + return mRenderThread; +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h index 97785a46dcd7..f39aa4b96547 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -16,8 +16,6 @@ #ifndef RENDERSTATE_H #define RENDERSTATE_H -#include "Caches.h" -#include "renderstate/PixelBufferState.h" #include "utils/Macros.h" #include <GLES2/gl2.h> @@ -34,7 +32,6 @@ class GrContext; namespace android { namespace uirenderer { -class Caches; class Layer; class DeferredLayerUpdater; @@ -44,22 +41,16 @@ class CanvasContext; class RenderThread; } -// TODO: Replace Cache's GL state tracking with this. For now it's more a thin // wrapper of Caches for users to migrate to. class RenderState { PREVENT_COPY_AND_ASSIGN(RenderState); friend class renderthread::RenderThread; - friend class Caches; friend class renderthread::CacheManager; public: - void onGLContextCreated(); - void onGLContextDestroyed(); + void onContextCreated(); + void onContextDestroyed(); - void onVkContextCreated(); - void onVkContextDestroyed(); - - void flush(Caches::FlushMode flushMode); void onBitmapDestroyed(uint32_t pixelRefId); void setViewport(GLsizei width, GLsizei height); @@ -70,8 +61,6 @@ public: GLuint createFramebuffer(); void deleteFramebuffer(GLuint fbo); - void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); - void debugOverdraw(bool enable, bool clear); void registerLayer(Layer* layer) { mActiveLayers.insert(layer); } @@ -101,16 +90,15 @@ public: void dump(); + renderthread::RenderThread& getRenderThread(); + private: - void interruptForFunctorInvoke(); - void resumeFromFunctorInvoke(); void destroyLayersInUpdater(); explicit RenderState(renderthread::RenderThread& thread); ~RenderState(); renderthread::RenderThread& mRenderThread; - Caches* mCaches = nullptr; std::set<Layer*> mActiveLayers; std::set<DeferredLayerUpdater*> mActiveLayerUpdaters; diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp deleted file mode 100644 index 470b4f5de97f..000000000000 --- a/libs/hwui/renderstate/TextureState.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "renderstate/TextureState.h" - -#include "Caches.h" -#include "utils/TraceUtils.h" - -#include <GLES3/gl3.h> -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <memory> - -namespace android { -namespace uirenderer { - -// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is -static const int SHADOW_LUT_SIZE = 128; - -// Must define as many texture units as specified by kTextureUnitsCount -const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3}; - -TextureState::TextureState() : mTextureUnit(0) { - glActiveTexture(kTextureUnits[0]); - resetBoundTextures(); - - GLint maxTextureUnits; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); - LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, - "At least %d texture units are required!", kTextureUnitsCount); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); -} - -TextureState::~TextureState() { - if (mShadowLutTexture != nullptr) { - mShadowLutTexture->deleteTexture(); - } -} - -/** - * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to - * darkness at that spot. Input values of 0->1 should be mapped within the same - * range, but can affect the curve for a different visual falloff. - * - * This is used to populate the shadow LUT texture for quick lookup in the - * shadow shader. - */ -static float computeShadowOpacity(float ratio) { - // exponential falloff function provided by UX - float val = 1 - ratio; - return exp(-val * val * 4.0) - 0.018; -} - -void TextureState::constructTexture(Caches& caches) { - if (mShadowLutTexture == nullptr) { - mShadowLutTexture.reset(new Texture(caches)); - - unsigned char bytes[SHADOW_LUT_SIZE]; - for (int i = 0; i < SHADOW_LUT_SIZE; i++) { - float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f); - bytes[i] = computeShadowOpacity(inputRatio) * 255; - } - mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes); - mShadowLutTexture->setFilter(GL_LINEAR); - mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE); - } -} - -void TextureState::activateTexture(GLuint textureUnit) { - LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount, - "Tried to use texture unit index %d, only %d exist", textureUnit, - kTextureUnitsCount); - if (mTextureUnit != textureUnit) { - glActiveTexture(kTextureUnits[textureUnit]); - mTextureUnit = textureUnit; - } -} - -void TextureState::resetActiveTexture() { - mTextureUnit = -1; -} - -void TextureState::bindTexture(GLuint texture) { - if (mBoundTextures[mTextureUnit] != texture) { - glBindTexture(GL_TEXTURE_2D, texture); - mBoundTextures[mTextureUnit] = texture; - } -} - -void TextureState::bindTexture(GLenum target, GLuint texture) { - if (target == GL_TEXTURE_2D) { - bindTexture(texture); - } else { - // GLConsumer directly calls glBindTexture() with - // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target - // since the cached state could be stale - glBindTexture(target, texture); - } -} - -void TextureState::deleteTexture(GLuint texture) { - // When glDeleteTextures() is called on a currently bound texture, - // OpenGL ES specifies that the texture is then considered unbound - // Consider the following series of calls: - // - // glGenTextures -> creates texture name 2 - // glBindTexture(2) - // glDeleteTextures(2) -> 2 is now unbound - // glGenTextures -> can return 2 again - // - // If we don't call glBindTexture(2) after the second glGenTextures - // call, any texture operation will be performed on the default - // texture (name=0) - - unbindTexture(texture); - - glDeleteTextures(1, &texture); -} - -void TextureState::resetBoundTextures() { - for (int i = 0; i < kTextureUnitsCount; i++) { - mBoundTextures[i] = 0; - } -} - -void TextureState::unbindTexture(GLuint texture) { - for (int i = 0; i < kTextureUnitsCount; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } -} - -} /* namespace uirenderer */ -} /* namespace android */ diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h deleted file mode 100644 index f1996d431fa2..000000000000 --- a/libs/hwui/renderstate/TextureState.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef RENDERSTATE_TEXTURESTATE_H -#define RENDERSTATE_TEXTURESTATE_H - -#include "Texture.h" -#include "Vertex.h" - -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> -#include <memory> - -namespace android { -namespace uirenderer { - -class Texture; - -class TextureState { - friend class Caches; // TODO: move to RenderState -public: - void constructTexture(Caches& caches); - - /** - * Activate the specified texture unit. The texture unit must - * be specified using an integer number (0 for GL_TEXTURE0 etc.) - */ - void activateTexture(GLuint textureUnit); - - /** - * Invalidate the cached value of the active texture unit. - */ - void resetActiveTexture(); - - /** - * Binds the specified texture as a GL_TEXTURE_2D texture. - * All texture bindings must be performed with this method or - * bindTexture(GLenum, GLuint). - */ - void bindTexture(GLuint texture); - - /** - * Binds the specified texture with the specified render target. - * All texture bindings must be performed with this method or - * bindTexture(GLuint). - */ - void bindTexture(GLenum target, GLuint texture); - - /** - * Deletes the specified texture and clears it from the cache - * of bound textures. - * All textures must be deleted using this method. - */ - void deleteTexture(GLuint texture); - - /** - * Signals that the cache of bound textures should be cleared. - * Other users of the context may have altered which textures are bound. - */ - void resetBoundTextures(); - - /** - * Clear the cache of bound textures. - */ - void unbindTexture(GLuint texture); - - Texture* getShadowLutTexture() { return mShadowLutTexture.get(); } - -private: - // total number of texture units available for use - static const int kTextureUnitsCount = 4; - - TextureState(); - ~TextureState(); - GLuint mTextureUnit; - - // Caches texture bindings for the GL_TEXTURE_2D target - GLuint mBoundTextures[kTextureUnitsCount]; - - std::unique_ptr<Texture> mShadowLutTexture; -}; - -} /* namespace uirenderer */ -} /* namespace android */ - -#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 82bfc4943526..7acc44ca65b7 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -21,6 +21,7 @@ #include "RenderThread.h" #include "pipeline/skia/ShaderCache.h" #include "pipeline/skia/SkiaMemoryTracer.h" +#include "Properties.h" #include "renderstate/RenderState.h" #include <GrContextOptions.h> @@ -215,11 +216,12 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) log.appendFormat(" Layer Info:\n"); } + const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL + ? "GlLayer" : "VkLayer"; size_t layerMemoryTotal = 0; for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin(); it != renderState->mActiveLayers.end(); it++) { const Layer* layer = *it; - const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer"; log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(), layer->getHeight()); layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5d7252304bf2..8b07d1dadeb6 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,7 +18,6 @@ #include <GpuMemoryTracker.h> #include "AnimationContext.h" -#include "Caches.h" #include "EglManager.h" #include "Frame.h" #include "LayerUpdateQueue.h" @@ -495,13 +494,6 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); -#ifdef BUGREPORT_FONT_CACHE_USAGE - auto renderType = Properties::getRenderPipelineType(); - if (RenderPipelineType::OpenGL == renderType) { - Caches& caches = Caches::getInstance(); - caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); - } -#endif } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index cd21822df5b1..5f8d7ad3373a 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -18,6 +18,7 @@ #include <cutils/properties.h> #include <log/log.h> +#include <private/gui/SyncFeatures.h> #include <utils/Trace.h> #include "utils/StringUtils.h" @@ -464,6 +465,109 @@ bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { return preserved; } +status_t EglManager::fenceWait(sp<Fence>& fence) { + if (!hasEglContext()) { + ALOGE("EglManager::fenceWait: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Block GPU on the fence. + // Create an EGLSyncKHR from the current fence. + int fenceFd = fence->dup(); + if (fenceFd == -1) { + ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, + EGL_NONE + }; + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(mEglDisplay, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(mEglDisplay, sync); + if (eglErr != EGL_SUCCESS) { + ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + // Block CPU on the fence. + status_t err = fence->waitForever("EglManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("EglManager::fenceWait: error waiting for fence: %d", err); + return err; + } + } + return OK; +} + +status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, + sp<Fence>& nativeFence) { + if (!hasEglContext()) { + ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized"); + return INVALID_OPERATION; + } + + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync); + eglDestroySyncKHR(mEglDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGE("EglManager::createReleaseFence: error dup'ing native fence " + "fd: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + nativeFence = new Fence(fenceFd); + *eglFence = EGL_NO_SYNC_KHR; + } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) { + if (*eglFence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000); + if (result == EGL_FALSE) { + ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(mEglDisplay, *eglFence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (*eglFence == EGL_NO_SYNC_KHR) { + ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + } + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 8e8bb8b68a1c..507673adf26e 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -17,8 +17,10 @@ #define EGLMANAGER_H #include <EGL/egl.h> +#include <EGL/eglext.h> #include <SkRect.h> #include <cutils/compiler.h> +#include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> @@ -66,6 +68,14 @@ public: EGLDisplay eglDisplay() const { return mEglDisplay; } + // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension + // support is missing, block the CPU on the fence. + status_t fenceWait(sp<Fence>& fence); + + // Creates a fence that is signaled, when all the pending GL commands are flushed. + // Depending on installed extensions, the result is either Android native fence or EGL fence. + status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence); + private: void initExtensions(); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 36ffaee2458b..2322fbf21f27 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -178,7 +178,7 @@ void RenderThread::requireGlContext() { return; } mEglManager->initialize(); - renderState().onGLContextCreated(); + renderState().onContextCreated(); #ifdef HWUI_GLES_WRAP_ENABLED debug::GlesDriver* driver = debug::GlesDriver::get(); @@ -202,7 +202,7 @@ void RenderThread::requireGlContext() { void RenderThread::destroyGlContext() { if (mEglManager->hasEglContext()) { setGrContext(nullptr); - renderState().onGLContextDestroyed(); + renderState().onContextDestroyed(); mEglManager->destroy(); } } diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index cc4b87adfd7b..038e13c513fd 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -40,7 +40,7 @@ namespace renderthread { VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - mRenderThread.renderState().onVkContextDestroyed(); + mRenderThread.renderState().onContextDestroyed(); mRenderThread.setGrContext(nullptr); if (VK_NULL_HANDLE != mCommandPool) { @@ -405,7 +405,7 @@ void VulkanManager::initialize() { mSwapBehavior = SwapBehavior::BufferAge; } - mRenderThread.renderState().onVkContextCreated(); + mRenderThread.renderState().onContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -982,6 +982,22 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } +status_t VulkanManager::fenceWait(sp<Fence>& fence) { + //TODO: Insert a wait on fence command into the Vulkan command buffer. + // Block CPU on the fence. + status_t err = fence->waitForever("VulkanManager::fenceWait"); + if (err != NO_ERROR) { + ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); + return err; + } + return OK; +} + +status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) { + //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. + return OK; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 5524c39d7a0c..ebc11a50685e 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -23,6 +23,8 @@ #include <vulkan/vulkan.h> #include <SkSurface.h> +#include <ui/Fence.h> +#include <utils/StrongPointer.h> #include <vk/GrVkBackendContext.h> class GrVkExtensions; @@ -110,6 +112,12 @@ public: // Presents the current VkImage. void swapBuffers(VulkanSurface* surface); + // Inserts a wait on fence command into the Vulkan command buffer. + status_t fenceWait(sp<Fence>& fence); + + // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. + status_t createReleaseFence(sp<Fence>& nativeFence); + private: friend class RenderThread; diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp new file mode 100644 index 000000000000..c8220c6cb0d4 --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.cpp @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <cutils/compiler.h> +#include <gui/BufferItem.h> +#include <gui/BufferQueue.h> +#include <private/gui/SyncFeatures.h> +#include "EGLConsumer.h" +#include "SurfaceTexture.h" + +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/Trace.h> + +#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" +#define EGL_PROTECTED_CONTENT_EXT 0x32C0 + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__) +#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +static const struct { + uint32_t width, height; + char const* bits; +} kDebugData = {15, 12, + "_______________" + "_______________" + "_____XX_XX_____" + "__X_X_____X_X__" + "__X_XXXXXXX_X__" + "__XXXXXXXXXXX__" + "___XX_XXX_XX___" + "____XXXXXXX____" + "_____X___X_____" + "____X_____X____" + "_______________" + "_______________"}; + +Mutex EGLConsumer::sStaticInitLock; +sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer; + +static bool hasEglProtectedContentImpl() { + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR); + size_t extsLen = strlen(exts); + bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts); + bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1); + bool atEnd = (cropExtLen + 1) < extsLen && + !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1)); + bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " "); + return equal || atStart || atEnd || inMiddle; +} + +static bool hasEglProtectedContent() { + // Only compute whether the extension is present once the first time this + // function is called. + static bool hasIt = hasEglProtectedContentImpl(); + return hasIt; +} + +EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {} + +status_t EGLConsumer::updateTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + return err; + } + + BufferItem item; + + // Acquire the next buffer. + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + err = st.acquireBufferLocked(&item, 0); + if (err != NO_ERROR) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + // We always bind the texture even if we don't update its contents. + EGC_LOGV("updateTexImage: no buffers were available"); + glBindTexture(st.mTexTarget, st.mTexName); + err = NO_ERROR; + } else { + EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); + } + return err; + } + + // Release the previous buffer. + err = updateAndReleaseLocked(item, nullptr, st); + if (err != NO_ERROR) { + // We always bind the texture. + glBindTexture(st.mTexTarget, st.mTexName); + return err; + } + + // Bind the new buffer to the GL texture, and wait until it's ready. + return bindTextureImageLocked(st); +} + +status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { + // Make sure the EGL state is the same as in previous calls. + status_t err = NO_ERROR; + + // if we're detached, no need to validate EGL's state -- we won't use it. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + err = checkAndUpdateEglStateLocked(st, true); + if (err != NO_ERROR) { + return err; + } + } + + // Update the EGLConsumer state. + int buf = st.mCurrentTexture; + if (buf != BufferQueue::INVALID_BUFFER_SLOT) { + EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode); + + // if we're detached, we just use the fence that was created in detachFromContext() + // so... basically, nothing more to do here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // Do whatever sync ops we need to do before releasing the slot. + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err); + return err; + } + } + + err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + if (err < NO_ERROR) { + EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); + return err; + } + + if (mReleasedTexImage == nullptr) { + mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); + } + + st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImage = mReleasedTexImage; + st.mCurrentCrop.makeInvalid(); + st.mCurrentTransform = 0; + st.mCurrentTimestamp = 0; + st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN; + st.mCurrentFence = Fence::NO_FENCE; + st.mCurrentFenceTime = FenceTime::NO_FENCE; + + // detached, don't touch the texture (and we may not even have an + // EGLDisplay here. + if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) { + // This binds a dummy buffer (mReleasedTexImage). + status_t result = bindTextureImageLocked(st); + if (result != NO_ERROR) { + return result; + } + } + } + + return NO_ERROR; +} + +sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() { + Mutex::Autolock _l(sStaticInitLock); + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { + // The first time, create the debug texture in case the application + // continues to use it. + sp<GraphicBuffer> buffer = new GraphicBuffer( + kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888, + GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]"); + uint32_t* bits; + buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits)); + uint32_t stride = buffer->getStride(); + uint32_t height = buffer->getHeight(); + memset(bits, 0, stride * height * 4); + for (uint32_t y = 0; y < kDebugData.height; y++) { + for (uint32_t x = 0; x < kDebugData.width; x++) { + bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000 + : 0xFFFFFFFF; + } + bits += stride; + } + buffer->unlock(); + sReleasedTexImageBuffer = buffer; + } + return sReleasedTexImageBuffer; +} + +void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior EglImage created is using a stale buffer. This + // replaces any old EglImage with a new one (using the new buffer). + int slot = item->mSlot; + if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } +} + +void EGLConsumer::onReleaseBufferLocked(int buf) { + mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st) { + status_t err = NO_ERROR; + + int slot = item.mSlot; + + if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { + EGC_LOGE( + "updateAndRelease: EGLConsumer is not attached to an OpenGL " + "ES context"); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return INVALID_OPERATION; + } + + // Confirm state. + err = checkAndUpdateEglStateLocked(st); + if (err != NO_ERROR) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return err; + } + + // Ensure we have a valid EglImageKHR for the slot, creating an EglImage + // if nessessary, for the gralloc buffer currently in the slot in + // ConsumerBase. + // We may have to do this even when item.mGraphicBuffer == NULL (which + // means the buffer was previously acquired). + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, + slot); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + return UNKNOWN_ERROR; + } + + // Do whatever sync ops we need to do before releasing the old slot. + if (slot != st.mCurrentTexture) { + err = syncForReleaseLocked(mEglDisplay, st); + if (err != NO_ERROR) { + // Release the buffer we just acquired. It's not safe to + // release the old buffer, so instead we just drop the new frame. + // As we are still under lock since acquireBuffer, it is safe to + // release by slot. + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, + EGL_NO_SYNC_KHR); + return err; + } + } + + EGC_LOGV( + "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture, + mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr, + slot, st.mSlots[slot].mGraphicBuffer->handle); + + // Hang onto the pointer so that it isn't freed in the call to + // releaseBufferLocked() if we're in shared buffer mode and both buffers are + // the same. + sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage; + + // release old buffer + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (pendingRelease == nullptr) { + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay, + mEglSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), + status); + err = status; + // keep going, with error raised [?] + } + } else { + pendingRelease->currentTexture = st.mCurrentTexture; + pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->display = mEglDisplay; + pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence; + pendingRelease->isPending = true; + } + } + + // Update the EGLConsumer state. + st.mCurrentTexture = slot; + mCurrentTextureImage = nextTextureImage; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + + st.computeCurrentTransformMatrixLocked(); + + return err; +} + +status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) { + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("bindTextureImage: invalid display"); + return INVALID_OPERATION; + } + + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error); + } + + glBindTexture(st.mTexTarget, st.mTexName); + if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { + EGC_LOGE("bindTextureImage: no currently-bound texture"); + return NO_INIT; + } + + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); + if (err != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + + // In the rare case that the display is terminated and then initialized + // again, we can't detect that the display changed (it didn't), but the + // image is invalid. In this case, repeat the exact same steps while + // forcing the creation of a new image. + if ((error = glGetError()) != GL_NO_ERROR) { + glBindTexture(st.mTexTarget, st.mTexName); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); + if (result != NO_ERROR) { + EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, + st.mCurrentTexture); + return UNKNOWN_ERROR; + } + mCurrentTextureImage->bindToTextureTarget(st.mTexTarget); + if ((error = glGetError()) != GL_NO_ERROR) { + EGC_LOGE("bindTextureImage: error binding external image: %#04x", error); + return UNKNOWN_ERROR; + } + } + + // Wait for the new buffer to be ready. + return doGLFenceWaitLocked(st); +} + +status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (!contextCheck) { + // if this is the first time we're called, mEglDisplay/mEglContext have + // never been set, so don't error out (below). + if (mEglDisplay == EGL_NO_DISPLAY) { + mEglDisplay = dpy; + } + if (mEglContext == EGL_NO_CONTEXT) { + mEglContext = ctx; + } + } + + if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) { + EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext"); + return INVALID_OPERATION; + } + + mEglDisplay = dpy; + mEglContext = ctx; + return NO_ERROR; +} + +status_t EGLConsumer::detachFromContext(SurfaceTexture& st) { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { + EGC_LOGE("detachFromContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { + EGC_LOGE("detachFromContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { + status_t err = syncForReleaseLocked(dpy, st); + if (err != OK) { + return err; + } + + glDeleteTextures(1, &st.mTexName); + } + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + + return OK; +} + +status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) { + // Initialize mCurrentTextureImage if there is a current buffer from past attached state. + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + if (!mEglSlots[slot].mEglImage.get()) { + mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer); + } + mCurrentTextureImage = mEglSlots[slot].mEglImage; + } + + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (dpy == EGL_NO_DISPLAY) { + EGC_LOGE("attachToContext: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (ctx == EGL_NO_CONTEXT) { + EGC_LOGE("attachToContext: invalid current EGLContext"); + return INVALID_OPERATION; + } + + // We need to bind the texture regardless of whether there's a current + // buffer. + glBindTexture(st.mTexTarget, GLuint(tex)); + + mEglDisplay = dpy; + mEglContext = ctx; + st.mTexName = tex; + st.mOpMode = SurfaceTexture::OpMode::attachedToGL; + + if (mCurrentTextureImage != nullptr) { + // This may wait for a buffer a second time. This is likely required if + // this is a different context, since otherwise the wait could be skipped + // by bouncing through another context. For the same context the extra + // wait is redundant. + status_t err = bindTextureImageLocked(st); + if (err != NO_ERROR) { + return err; + } + } + + return OK; +} + +status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) { + EGC_LOGV("syncForReleaseLocked"); + + if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + if (SyncFeatures::getInstance().useNativeFenceSync()) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync); + eglDestroySyncKHR(dpy, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + EGC_LOGE( + "syncForReleaseLocked: error dup'ing native fence " + "fd: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } + sp<Fence> fence(new Fence(fenceFd)); + status_t err = st.addReleaseFenceLocked(st.mCurrentTexture, + mCurrentTextureImage->graphicBuffer(), fence); + if (err != OK) { + EGC_LOGE( + "syncForReleaseLocked: error adding release fence: " + "%s (%d)", + strerror(-err), err); + return err; + } + } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) { + EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence; + if (fence != EGL_NO_SYNC_KHR) { + // There is already a fence for the current slot. We need to + // wait on that before replacing it with another fence to + // ensure that all outstanding buffer accesses have completed + // before the producer accesses it. + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + if (result == EGL_FALSE) { + EGC_LOGE( + "syncForReleaseLocked: error waiting for previous " + "fence: %#x", + eglGetError()); + return UNKNOWN_ERROR; + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + EGC_LOGE( + "syncForReleaseLocked: timeout waiting for previous " + "fence"); + return TIMED_OUT; + } + eglDestroySyncKHR(dpy, fence); + } + + // Create a fence for the outstanding accesses in the current + // OpenGL ES context. + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (fence == EGL_NO_SYNC_KHR) { + EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + glFlush(); + mEglSlots[st.mCurrentTexture].mEglFence = fence; + } + } + + return OK; +} + +status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLContext ctx = eglGetCurrentContext(); + + if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) { + EGC_LOGE("doGLFenceWait: invalid current EGLDisplay"); + return INVALID_OPERATION; + } + + if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) { + EGC_LOGE("doGLFenceWait: invalid current EGLContext"); + return INVALID_OPERATION; + } + + if (st.mCurrentFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync() && + SyncFeatures::getInstance().useNativeFenceSync()) { + // Create an EGLSyncKHR from the current fence. + int fenceFd = st.mCurrentFence->dup(); + if (fenceFd == -1) { + EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno); + return -errno; + } + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + close(fenceFd); + EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError()); + return UNKNOWN_ERROR; + } + + // XXX: The spec draft is inconsistent as to whether this should + // return an EGLint or void. Ignore the return value for now, as + // it's not strictly needed. + eglWaitSyncKHR(dpy, sync, 0); + EGLint eglErr = eglGetError(); + eglDestroySyncKHR(dpy, sync); + if (eglErr != EGL_SUCCESS) { + EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr); + return UNKNOWN_ERROR; + } + } else { + status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked"); + if (err != NO_ERROR) { + EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +void EGLConsumer::onFreeBufferLocked(int slotIndex) { + mEglSlots[slotIndex].mEglImage.clear(); +} + +void EGLConsumer::onAbandonLocked() { + mCurrentTextureImage.clear(); +} + +EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) + : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {} + +EGLConsumer::EglImage::~EglImage() { + if (mEglImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("~EglImage: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + } +} + +status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) { + // If there's an image and it's no longer valid, destroy it. + bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; + bool displayInvalid = mEglDisplay != eglDisplay; + if (haveImage && (displayInvalid || forceCreation)) { + if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { + ALOGE("createIfNeeded: eglDestroyImageKHR failed"); + } + eglTerminate(mEglDisplay); + mEglImage = EGL_NO_IMAGE_KHR; + mEglDisplay = EGL_NO_DISPLAY; + } + + // If there's no image, create one. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = eglDisplay; + mEglImage = createImage(mEglDisplay, mGraphicBuffer); + } + + // Fail if we can't create a valid image. + if (mEglImage == EGL_NO_IMAGE_KHR) { + mEglDisplay = EGL_NO_DISPLAY; + const sp<GraphicBuffer>& buffer = mGraphicBuffer; + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), + buffer->getPixelFormat()); + return UNKNOWN_ERROR; + } + + return OK; +} + +void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { + glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage)); +} + +EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy, + const sp<GraphicBuffer>& graphicBuffer) { + EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); + const bool createProtectedImage = + (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, + EGL_TRUE, + createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + createProtectedImage ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + eglInitialize(dpy, nullptr, nullptr); + EGLImageKHR image = + eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + if (image == EGL_NO_IMAGE_KHR) { + EGLint error = eglGetError(); + ALOGE("error creating EGLImage: %#x", error); + eglTerminate(dpy); + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h new file mode 100644 index 000000000000..eccb08298f6f --- /dev/null +++ b/libs/hwui/surfacetexture/EGLConsumer.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/BufferQueueDefs.h> + +#include <ui/FenceTime.h> +#include <ui/GraphicBuffer.h> +#include <utils/Mutex.h> + +namespace android { + +class SurfaceTexture; + +/* + * EGLConsumer implements the parts of SurfaceTexture that deal with + * textures attached to an GL context. + */ +class EGLConsumer { +public: + EGLConsumer(); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(SurfaceTexture& st); + + /* + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(SurfaceTexture& st); + + /** + * detachFromContext detaches the EGLConsumer from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a EGLConsumer from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a EGLConsumer has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the EGLConsumer is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(SurfaceTexture& st); + + /** + * attachToContext attaches a EGLConsumer that is currently in the + * 'detached' state to the current OpenGL ES context. A EGLConsumer is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * EGLConsumer that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex, SurfaceTexture& st); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + + /** + * onAbandonLocked amends the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + void onAbandonLocked(); + +protected: + struct PendingRelease { + PendingRelease() + : isPending(false) + , currentTexture(-1) + , graphicBuffer() + , display(nullptr) + , fence(nullptr) {} + + bool isPending; + int currentTexture; + sp<GraphicBuffer> graphicBuffer; + EGLDisplay display; + EGLSyncKHR fence; + }; + + /** + * This releases the buffer in the slot referenced by mCurrentTexture, + * then updates state to refer to the BufferItem, which must be a + * newly-acquired buffer. If pendingRelease is not null, the parameters + * which would have been passed to releaseBufferLocked upon the successful + * completion of the method will instead be returned to the caller, so that + * it may call releaseBufferLocked itself later. + */ + status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease, + SurfaceTexture& st); + + /** + * Binds mTexName and the current buffer to mTexTarget. Uses + * mCurrentTexture if it's set, mCurrentTextureImage if not. If the + * bind succeeds, this calls doGLFenceWait. + */ + status_t bindTextureImageLocked(SurfaceTexture& st); + + /** + * Gets the current EGLDisplay and EGLContext values, and compares them + * to mEglDisplay and mEglContext. If the fields have been previously + * set, the values must match; if not, the fields are set to the current + * values. + * The contextCheck argument is used to ensure that a GL context is + * properly set; when set to false, the check is not performed. + */ + status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false); + + /** + * EglImage is a utility class for tracking and creating EGLImageKHRs. There + * is primarily just one image per slot, but there is also special cases: + * - For releaseTexImage, we use a debug image (mReleasedTexImage) + * - After freeBuffer, we must still keep the current image/buffer + * Reference counting EGLImages lets us handle all these cases easily while + * also only creating new EGLImages from buffers when required. + */ + class EglImage : public LightRefBase<EglImage> { + public: + EglImage(sp<GraphicBuffer> graphicBuffer); + + /** + * createIfNeeded creates an EGLImage if required (we haven't created + * one yet, or the EGLDisplay or crop-rect has changed). + */ + status_t createIfNeeded(EGLDisplay display, bool forceCreate = false); + + /** + * This calls glEGLImageTargetTexture2DOES to bind the image to the + * texture in the specified texture target. + */ + void bindToTextureTarget(uint32_t texTarget); + + const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } + const native_handle* graphicBufferHandle() { + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; + } + + private: + // Only allow instantiation using ref counting. + friend class LightRefBase<EglImage>; + virtual ~EglImage(); + + // createImage creates a new EGLImage from a GraphicBuffer. + EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); + + // Disallow copying + EglImage(const EglImage& rhs); + void operator=(const EglImage& rhs); + + // mGraphicBuffer is the buffer that was used to create this image. + sp<GraphicBuffer> mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEGLDisplay is the EGLDisplay that was used to create mEglImage. + EGLDisplay mEglDisplay; + + // mCropRect is the crop rectangle passed to EGL when mEglImage + // was created. + Rect mCropRect; + }; + + /** + * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command + * stream to ensure that it is safe for future OpenGL ES commands to + * access the current texture buffer. + */ + status_t doGLFenceWaitLocked(SurfaceTexture& st) const; + + /** + * syncForReleaseLocked performs the synchronization needed to release the + * current slot from an OpenGL ES context. If needed it will set the + * current slot's fence to guard against a producer accessing the buffer + * before the outstanding accesses have completed. + */ + status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st); + + /** + * returns a graphic buffer used when the texture image has been released + */ + static sp<GraphicBuffer> getDebugTexImageBuffer(); + + /** + * The default consumer usage flags that EGLConsumer always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the EGLConsumer user. In particular, EGLConsumer will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentTextureImage is the EglImage/buffer of the current texture. It's + * possible that this buffer is not associated with any buffer slot, so we + * must track it separately in order to support the getCurrentBuffer method. + */ + sp<EglImage> mCurrentTextureImage; + + /** + * EGLSlot contains the information and object references that + * EGLConsumer maintains about a BufferQueue buffer slot. + */ + struct EglSlot { + EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + /** + * mEglImage is the EGLImage created from mGraphicBuffer. + */ + sp<EglImage> mEglImage; + + /** + * mFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. It is initialized + * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + * on a compile-time option) set to a new sync object in updateTexImage. + */ + EGLSyncKHR mEglFence; + }; + + /** + * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently + * associated. It is intialized to EGL_NO_DISPLAY and gets set to the + * current display when updateTexImage is called for the first time and when + * attachToContext is called. + */ + EGLDisplay mEglDisplay; + + /** + * mEglContext is the OpenGL ES context with which this EGLConsumer is + * currently associated. It is initialized to EGL_NO_CONTEXT and gets set + * to the current GL context when updateTexImage is called for the first + * time and when attachToContext is called. + */ + EGLContext mEglContext; + + /** + * mEGLSlots stores the buffers that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; + + /** + * protects static initialization + */ + static Mutex sStaticInitLock; + + /** + * mReleasedTexImageBuffer is a dummy buffer used when in single buffer + * mode and releaseTexImage() has been called + */ + static sp<GraphicBuffer> sReleasedTexImageBuffer; + sp<EglImage> mReleasedTexImage; +}; + +}; // namespace android diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp new file mode 100644 index 000000000000..9ffccfb4d340 --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ImageConsumer.h" +#include <gui/BufferQueue.h> +#include "Properties.h" +#include "SurfaceTexture.h" +#include "renderstate/RenderState.h" +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "renderthread/VulkanManager.h" + +// Macro for including the SurfaceTexture name in log messages +#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__) + +namespace android { + +void ImageConsumer::onFreeBufferLocked(int slotIndex) { + mImageSlots[slotIndex].mImage.reset(); +} + +void ImageConsumer::onAcquireBufferLocked(BufferItem* item) { + // If item->mGraphicBuffer is not null, this buffer has not been acquired + // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage. + if (item->mGraphicBuffer != nullptr) { + mImageSlots[item->mSlot].mImage.reset(); + } +} + +void ImageConsumer::onReleaseBufferLocked(int buf) { + mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR; +} + +void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) { + if (!mImage.get()) { + mImage = graphicBuffer.get() + ? SkImage::MakeFromAHardwareBuffer( + reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()), + kPremul_SkAlphaType) + : nullptr; + } +} + +sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st, + uirenderer::RenderState& renderState) { + BufferItem item; + status_t err; + err = st.acquireBufferLocked(&item, 0); + if (err != OK) { + if (err != BufferQueue::NO_BUFFER_AVAILABLE) { + IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } else { + int slot = st.mCurrentTexture; + if (slot != BufferItem::INVALID_BUFFER_SLOT) { + *queueEmpty = true; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; + } + } + return nullptr; + } + + int slot = item.mSlot; + if (item.mFence->isValid()) { + // Wait on the producer fence for the buffer to be ready. + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + err = renderState.getRenderThread().eglManager().fenceWait(item.mFence); + } else { + err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence); + } + if (err != OK) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Release old buffer. + if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) { + // If needed, set the released slot's fence to guard against a producer accessing the + // buffer before the outstanding accesses have completed. + sp<Fence> releaseFence; + EGLDisplay display = EGL_NO_DISPLAY; + if (uirenderer::Properties::getRenderPipelineType() == + uirenderer::RenderPipelineType::SkiaGL) { + auto& eglManager = renderState.getRenderThread().eglManager(); + display = eglManager.eglDisplay(); + err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence, + releaseFence); + } else { + err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence); + } + if (OK != err) { + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + + if (releaseFence.get()) { + status_t err = st.addReleaseFenceLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence); + if (err != OK) { + IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR); + return nullptr; + } + } + + // Finally release the old buffer. + status_t status = st.releaseBufferLocked( + st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display, + mImageSlots[st.mCurrentTexture].mEglFence); + if (status < NO_ERROR) { + IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status); + err = status; + // Keep going, with error raised. + } + } + + // Update the state. + st.mCurrentTexture = slot; + st.mCurrentCrop = item.mCrop; + st.mCurrentTransform = item.mTransform; + st.mCurrentScalingMode = item.mScalingMode; + st.mCurrentTimestamp = item.mTimestamp; + st.mCurrentDataSpace = item.mDataSpace; + st.mCurrentFence = item.mFence; + st.mCurrentFenceTime = item.mFenceTime; + st.mCurrentFrameNumber = item.mFrameNumber; + st.computeCurrentTransformMatrixLocked(); + + *queueEmpty = false; + mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer); + return mImageSlots[slot].mImage; +} + +} /* namespace android */ diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h new file mode 100644 index 000000000000..31ee8db52874 --- /dev/null +++ b/libs/hwui/surfacetexture/ImageConsumer.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/BufferQueueDefs.h> + +#include <SkImage.h> +#include <cutils/compiler.h> +#include <gui/BufferItem.h> +#include <system/graphics.h> + +namespace android { + +namespace uirenderer { +class RenderState; +} + +class SurfaceTexture; + +/* + * ImageConsumer implements the parts of SurfaceTexture that deal with + * images consumed by HWUI view system. + */ +class ImageConsumer { +public: + sk_sp<SkImage> dequeueImage(bool* queueEmpty, SurfaceTexture& cb, + uirenderer::RenderState& renderState); + + /** + * onAcquireBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase behavior. + */ + void onAcquireBufferLocked(BufferItem* item); + + /** + * onReleaseBufferLocked amends the ConsumerBase method to update the + * mImageSlots array in addition to the ConsumerBase. + */ + void onReleaseBufferLocked(int slot); + + /** + * onFreeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the SkImage in that slot. Otherwise it has no effect. + */ + void onFreeBufferLocked(int slotIndex); + +private: + /** + * ImageSlot contains the information and object references that + * ImageConsumer maintains about a BufferQueue buffer slot. + */ + struct ImageSlot { + ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {} + + // mImage is the SkImage created from mGraphicBuffer. + sk_sp<SkImage> mImage; + + /** + * mEglFence is the EGL sync object that must signal before the buffer + * associated with this buffer slot may be dequeued. + */ + EGLSyncKHR mEglFence; + + void createIfNeeded(sp<GraphicBuffer> graphicBuffer); + }; + + /** + * ImageConsumer stores the SkImages that have been allocated by the BufferQueue + * for each buffer slot. It is initialized to null pointers, and gets + * filled in with the result of BufferQueue::acquire when the + * client dequeues a buffer from a + * slot that has not yet been used. The buffer allocated to a slot will also + * be replaced if the requested buffer usage or geometry differs from that + * of the buffer allocated to a slot. + */ + ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS]; +}; + +}; /* namespace android */ diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp new file mode 100644 index 000000000000..4bff715822e8 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/compiler.h> +#include <gui/BufferQueue.h> +#include <math/mat4.h> +#include <system/window.h> + +#include <utils/Trace.h> + +#include "Matrix.h" +#include "SurfaceTexture.h" + +namespace android { + +// Macros for including the SurfaceTexture name in log messages +#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__) +#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__) + +static const mat4 mtxIdentity; + +SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, + uint32_t texTarget, bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(tex) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::attachedToGL) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, + bool useFenceSync, bool isControlledByApp) + : ConsumerBase(bq, isControlledByApp) + , mCurrentCrop(Rect::EMPTY_RECT) + , mCurrentTransform(0) + , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE) + , mCurrentFence(Fence::NO_FENCE) + , mCurrentTimestamp(0) + , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN) + , mCurrentFrameNumber(0) + , mDefaultWidth(1) + , mDefaultHeight(1) + , mFilteringEnabled(true) + , mTexName(0) + , mUseFenceSync(useFenceSync) + , mTexTarget(texTarget) + , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) + , mOpMode(OpMode::detached) { + SFT_LOGV("SurfaceTexture"); + + memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix)); + + mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS); +} + +status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!"); + return NO_INIT; + } + mDefaultWidth = w; + mDefaultHeight = h; + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t SurfaceTexture::updateTexImage() { + ATRACE_CALL(); + SFT_LOGV("updateTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.updateTexImage(*this); +} + +status_t SurfaceTexture::releaseTexImage() { + // releaseTexImage can be invoked even when not attached to a GL context. + ATRACE_CALL(); + SFT_LOGV("releaseTexImage"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!"); + return NO_INIT; + } + + return mEGLConsumer.releaseTexImage(*this); +} + +status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber) { + status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber); + if (err != NO_ERROR) { + return err; + } + + switch (mOpMode) { + case OpMode::attachedToView: + mImageConsumer.onAcquireBufferLocked(item); + break; + case OpMode::attachedToGL: + mEGLConsumer.onAcquireBufferLocked(item, *this); + break; + case OpMode::detached: + break; + } + + return NO_ERROR; +} + +status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) { + // release the buffer if it hasn't already been discarded by the + // BufferQueue. This can happen, for example, when the producer of this + // buffer has reallocated the original buffer slot after this buffer + // was acquired. + status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence); + // We could be releasing an EGL buffer, even if not currently attached to a GL context. + mImageConsumer.onReleaseBufferLocked(buf); + mEGLConsumer.onReleaseBufferLocked(buf); + return err; +} + +status_t SurfaceTexture::detachFromContext() { + ATRACE_CALL(); + SFT_LOGV("detachFromContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::attachedToGL) { + SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context"); + return INVALID_OPERATION; + } + + status_t err = mEGLConsumer.detachFromContext(*this); + if (err == OK) { + mOpMode = OpMode::detached; + } + + return err; +} + +status_t SurfaceTexture::attachToContext(uint32_t tex) { + ATRACE_CALL(); + SFT_LOGV("attachToContext"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + SFT_LOGE("attachToContext: abandoned SurfaceTexture"); + return NO_INIT; + } + + if (mOpMode != OpMode::detached) { + SFT_LOGE( + "attachToContext: SurfaceTexture is already attached to a " + "context"); + return INVALID_OPERATION; + } + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible ImageConsumer cache + mImageConsumer.onFreeBufferLocked(mCurrentTexture); + } + + return mEGLConsumer.attachToContext(tex, *this); +} + +void SurfaceTexture::attachToView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + if (mAbandoned) { + SFT_LOGE("attachToView: abandoned SurfaceTexture"); + return; + } + if (mOpMode == OpMode::detached) { + mOpMode = OpMode::attachedToView; + + if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + // release possible EGLConsumer texture cache + mEGLConsumer.onFreeBufferLocked(mCurrentTexture); + mEGLConsumer.onAbandonLocked(); + } + } else { + SFT_LOGE("attachToView: already attached"); + } +} + +void SurfaceTexture::detachFromView() { + ATRACE_CALL(); + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("detachFromView: abandoned SurfaceTexture"); + return; + } + + if (mOpMode == OpMode::attachedToView) { + mOpMode = OpMode::detached; + } else { + SFT_LOGE("detachFromView: not attached to View"); + } +} + +uint32_t SurfaceTexture::getCurrentTextureTarget() const { + return mTexTarget; +} + +void SurfaceTexture::getTransformMatrix(float mtx[16]) { + Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void SurfaceTexture::setFilteringEnabled(bool enabled) { + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!"); + return; + } + bool needsRecompute = mFilteringEnabled != enabled; + mFilteringEnabled = enabled; + + if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { + SFT_LOGD("setFilteringEnabled called with no current item"); + } + + if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { + computeCurrentTransformMatrixLocked(); + } +} + +void SurfaceTexture::computeCurrentTransformMatrixLocked() { + SFT_LOGV("computeCurrentTransformMatrixLocked"); + sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) + ? nullptr + : mSlots[mCurrentTexture].mGraphicBuffer; + if (buf == nullptr) { + SFT_LOGD("computeCurrentTransformMatrixLocked: no current item"); + } + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, + mFilteringEnabled); +} + +void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf, + const Rect& cropRect, uint32_t transform, + bool filtering) { + // Transform matrices + static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); + static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); + + mat4 xform; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + xform *= mtxFlipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + xform *= mtxFlipV; + } + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + xform *= mtxRot90; + } + + if (!cropRect.isEmpty() && buf.get()) { + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + float shrinkAmount = 0.0f; + if (filtering) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight; + } + + mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1); + xform = crop * xform; + } + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so SurfaceTexture must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + xform = mtxFlipV * xform; + + memcpy(outTransform, xform.asArray(), sizeof(xform)); +} + +Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { + Rect outCrop = crop; + + uint32_t newWidth = static_cast<uint32_t>(crop.width()); + uint32_t newHeight = static_cast<uint32_t>(crop.height()); + + if (newWidth * bufferHeight > newHeight * bufferWidth) { + newWidth = newHeight * bufferWidth / bufferHeight; + ALOGV("too wide: newWidth = %d", newWidth); + } else if (newWidth * bufferHeight < newHeight * bufferWidth) { + newHeight = newWidth * bufferHeight / bufferWidth; + ALOGV("too tall: newHeight = %d", newHeight); + } + + uint32_t currentWidth = static_cast<uint32_t>(crop.width()); + uint32_t currentHeight = static_cast<uint32_t>(crop.height()); + + // The crop is too wide + if (newWidth < currentWidth) { + uint32_t dw = currentWidth - newWidth; + auto halfdw = dw / 2; + outCrop.left += halfdw; + // Not halfdw because it would subtract 1 too few when dw is odd + outCrop.right -= (dw - halfdw); + // The crop is too tall + } else if (newHeight < currentHeight) { + uint32_t dh = currentHeight - newHeight; + auto halfdh = dh / 2; + outCrop.top += halfdh; + // Not halfdh because it would subtract 1 too few when dh is odd + outCrop.bottom -= (dh - halfdh); + } + + ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right, + outCrop.bottom); + + return outCrop; +} + +nsecs_t SurfaceTexture::getTimestamp() { + SFT_LOGV("getTimestamp"); + Mutex::Autolock lock(mMutex); + return mCurrentTimestamp; +} + +android_dataspace SurfaceTexture::getCurrentDataSpace() { + SFT_LOGV("getCurrentDataSpace"); + Mutex::Autolock lock(mMutex); + return mCurrentDataSpace; +} + +uint64_t SurfaceTexture::getFrameNumber() { + SFT_LOGV("getFrameNumber"); + Mutex::Autolock lock(mMutex); + return mCurrentFrameNumber; +} + +Rect SurfaceTexture::getCurrentCrop() const { + Mutex::Autolock lock(mMutex); + return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) + ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight) + : mCurrentCrop; +} + +uint32_t SurfaceTexture::getCurrentTransform() const { + Mutex::Autolock lock(mMutex); + return mCurrentTransform; +} + +uint32_t SurfaceTexture::getCurrentScalingMode() const { + Mutex::Autolock lock(mMutex); + return mCurrentScalingMode; +} + +sp<Fence> SurfaceTexture::getCurrentFence() const { + Mutex::Autolock lock(mMutex); + return mCurrentFence; +} + +std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const { + Mutex::Autolock lock(mMutex); + return mCurrentFenceTime; +} + +void SurfaceTexture::freeBufferLocked(int slotIndex) { + SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); + if (slotIndex == mCurrentTexture) { + mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + } + // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure. + // Buffers can be freed after SurfaceTexture has detached from GL context or View. + mImageConsumer.onFreeBufferLocked(slotIndex); + mEGLConsumer.onFreeBufferLocked(slotIndex); + ConsumerBase::freeBufferLocked(slotIndex); +} + +void SurfaceTexture::abandonLocked() { + SFT_LOGV("abandonLocked"); + mEGLConsumer.onAbandonLocked(); + ConsumerBase::abandonLocked(); +} + +status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) { + return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS); +} + +void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat( + "%smTexName=%d mCurrentTexture=%d\n" + "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n", + prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top, + mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform); + + ConsumerBase::dumpLocked(result, prefix); +} + +sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, + uirenderer::RenderState& renderState) { + Mutex::Autolock _l(mMutex); + + if (mAbandoned) { + SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!"); + return nullptr; + } + + if (mOpMode != OpMode::attachedToView) { + SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View"); + return nullptr; + } + + auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState); + if (image.get()) { + uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix); + dataSpace = mCurrentDataSpace; + } + return image; +} + +}; // namespace android diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h new file mode 100644 index 000000000000..db392a9f8476 --- /dev/null +++ b/libs/hwui/surfacetexture/SurfaceTexture.h @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gui/BufferQueueDefs.h> +#include <gui/ConsumerBase.h> + +#include <ui/FenceTime.h> +#include <ui/GraphicBuffer.h> + +#include <utils/Mutex.h> +#include <utils/String8.h> + +#include "EGLConsumer.h" +#include "ImageConsumer.h" + +namespace android { + +namespace uirenderer { +class RenderState; +} + +/* + * SurfaceTexture consumes buffers of graphics data from a BufferQueue, + * and makes them available to HWUI render thread as a SkImage and to + * an application GL render thread as an OpenGL texture. + * + * When attached to an application GL render thread, a typical usage + * pattern is to set up the SurfaceTexture with the + * desired options, and call updateTexImage() when a new frame is desired. + * If a new frame is available, the texture will be updated. If not, + * the previous contents are retained. + * + * When attached to a HWUI render thread, the TextureView implementation + * calls dequeueImage, which either pulls a new SkImage or returns the + * last cached SkImage if BufferQueue is empty. + * When attached to HWUI render thread, SurfaceTexture is compatible to + * both Vulkan and GL drawing pipelines. + */ +class ANDROID_API SurfaceTexture : public ConsumerBase { +public: + enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES + typedef ConsumerBase::FrameAvailableListener FrameAvailableListener; + + /** + * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with + * the tex parameter is used, tex indicates the name of the OpenGL ES + * texture to which images are to be streamed. texTarget specifies the + * OpenGL ES texture target to which the texture will be bound in + * updateTexImage. useFenceSync specifies whether fences should be used to + * synchronize access to buffers if that behavior is enabled at + * compile-time. + * + * A SurfaceTexture may be detached from one OpenGL ES context and then + * attached to a different context using the detachFromContext and + * attachToContext methods, respectively. The intention of these methods is + * purely to allow a SurfaceTexture to be transferred from one consumer + * context to another. If such a transfer is not needed there is no + * requirement that either of these methods be called. + * + * If the constructor with the tex parameter is used, the SurfaceTexture is + * created in a state where it is considered attached to an OpenGL ES + * context for the purposes of the attachToContext and detachFromContext + * methods. However, despite being considered "attached" to a context, the + * specific OpenGL ES context doesn't get latched until the first call to + * updateTexImage. After that point, all calls to updateTexImage must be + * made with the same OpenGL ES context current. + * + * If the constructor without the tex parameter is used, the SurfaceTexture is + * created in a detached state, and attachToContext must be called before + * calls to updateTexImage. + */ + SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texureTarget, + bool useFenceSync, bool isControlledByApp); + + SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget, bool useFenceSync, + bool isControlledByApp); + + /** + * updateTexImage acquires the most recently queued buffer, and sets the + * image contents of the target texture to it. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + * + * This calls doGLFenceWait to ensure proper synchronization. + */ + status_t updateTexImage(); + + /** + * releaseTexImage releases the texture acquired in updateTexImage(). + * This is intended to be used in single buffer mode. + * + * This call may only be made while the OpenGL ES context to which the + * target texture belongs is bound to the calling thread. + */ + status_t releaseTexImage(); + + /** + * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix + * associated with the texture image set by the most recent call to + * updateTexImage. + * + * This transform matrix maps 2D homogeneous texture coordinates of the form + * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture + * coordinate that should be used to sample that location from the texture. + * Sampling the texture outside of the range of this transform is undefined. + * + * This transform is necessary to compensate for transforms that the stream + * content producer may implicitly apply to the content. By forcing users of + * a SurfaceTexture to apply this transform we avoid performing an extra + * copy of the data that would be needed to hide the transform from the + * user. + * + * The matrix is stored in column-major order so that it may be passed + * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv + * functions. + */ + void getTransformMatrix(float mtx[16]); + + /** + * Computes the transform matrix documented by getTransformMatrix + * from the BufferItem sub parts. + */ + static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf, + const Rect& cropRect, uint32_t transform, bool filtering); + + /** + * Scale the crop down horizontally or vertically such that it has the + * same aspect ratio as the buffer does. + */ + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + + /** + * getTimestamp retrieves the timestamp associated with the texture image + * set by the most recent call to updateTexImage. + * + * The timestamp is in nanoseconds, and is monotonically increasing. Its + * other semantics (zero point, etc) are source-dependent and should be + * documented by the source. + */ + int64_t getTimestamp(); + + /** + * getDataSpace retrieves the DataSpace associated with the texture image + * set by the most recent call to updateTexImage. + */ + android_dataspace getCurrentDataSpace(); + + /** + * getFrameNumber retrieves the frame number associated with the texture + * image set by the most recent call to updateTexImage. + * + * The frame number is an incrementing counter set to 0 at the creation of + * the BufferQueue associated with this consumer. + */ + uint64_t getFrameNumber(); + + /** + * setDefaultBufferSize is used to set the size of buffers returned by + * requestBuffers when a with and height of zero is requested. + * A call to setDefaultBufferSize() may trigger requestBuffers() to + * be called from the client. + * The width and height parameters must be no greater than the minimum of + * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + * An error due to invalid dimensions might not be reported until + * updateTexImage() is called. + */ + status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + /** + * setFilteringEnabled sets whether the transform matrix should be computed + * for use with bilinear filtering. + */ + void setFilteringEnabled(bool enabled); + + /** + * getCurrentTextureTarget returns the texture target of the current + * texture as returned by updateTexImage(). + */ + uint32_t getCurrentTextureTarget() const; + + /** + * getCurrentCrop returns the cropping rectangle of the current buffer. + */ + Rect getCurrentCrop() const; + + /** + * getCurrentTransform returns the transform of the current buffer. + */ + uint32_t getCurrentTransform() const; + + /** + * getCurrentScalingMode returns the scaling mode of the current buffer. + */ + uint32_t getCurrentScalingMode() const; + + /** + * getCurrentFence returns the fence indicating when the current buffer is + * ready to be read from. + */ + sp<Fence> getCurrentFence() const; + + /** + * getCurrentFence returns the FenceTime indicating when the current + * buffer is ready to be read from. + */ + std::shared_ptr<FenceTime> getCurrentFenceTime() const; + + /** + * setConsumerUsageBits overrides the ConsumerBase method to OR + * DEFAULT_USAGE_FLAGS to usage. + */ + status_t setConsumerUsageBits(uint64_t usage); + + /** + * detachFromContext detaches the SurfaceTexture from the calling thread's + * current OpenGL ES context. This context must be the same as the context + * that was current for previous calls to updateTexImage. + * + * Detaching a SurfaceTexture from an OpenGL ES context will result in the + * deletion of the OpenGL ES texture object into which the images were being + * streamed. After a SurfaceTexture has been detached from the OpenGL ES + * context calls to updateTexImage will fail returning INVALID_OPERATION + * until the SurfaceTexture is attached to a new OpenGL ES context using the + * attachToContext method. + */ + status_t detachFromContext(); + + /** + * attachToContext attaches a SurfaceTexture that is currently in the + * 'detached' state to the current OpenGL ES context. A SurfaceTexture is + * in the 'detached' state iff detachFromContext has successfully been + * called and no calls to attachToContext have succeeded since the last + * detachFromContext call. Calls to attachToContext made on a + * SurfaceTexture that is not in the 'detached' state will result in an + * INVALID_OPERATION error. + * + * The tex argument specifies the OpenGL ES texture object name in the + * new context into which the image contents will be streamed. A successful + * call to attachToContext will result in this texture object being bound to + * the texture target and populated with the image contents that were + * current at the time of the last call to detachFromContext. + */ + status_t attachToContext(uint32_t tex); + + sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace, + bool* queueEmpty, uirenderer::RenderState& renderState); + + /** + * attachToView attaches a SurfaceTexture that is currently in the + * 'detached' state to HWUI View system. + */ + void attachToView(); + + /** + * detachFromView detaches a SurfaceTexture from HWUI View system. + */ + void detachFromView(); + +protected: + /** + * abandonLocked overrides the ConsumerBase method to clear + * mCurrentTextureImage in addition to the ConsumerBase behavior. + */ + virtual void abandonLocked(); + + /** + * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture- + * specific info in addition to the ConsumerBase behavior. + */ + virtual void dumpLocked(String8& result, const char* prefix) const override; + + /** + * acquireBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase behavior. + */ + virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen, + uint64_t maxFrameNumber = 0) override; + + /** + * releaseBufferLocked overrides the ConsumerBase method to update the + * mEglSlots array in addition to the ConsumerBase. + */ + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display, EGLSyncKHR eglFence) override; + + /** + * freeBufferLocked frees up the given buffer slot. If the slot has been + * initialized this will release the reference to the GraphicBuffer in that + * slot and destroy the EGLImage in that slot. Otherwise it has no effect. + * + * This method must be called with mMutex locked. + */ + virtual void freeBufferLocked(int slotIndex); + + /** + * computeCurrentTransformMatrixLocked computes the transform matrix for the + * current texture. It uses mCurrentTransform and the current GraphicBuffer + * to compute this matrix and stores it in mCurrentTransformMatrix. + * mCurrentTextureImage must not be NULL. + */ + void computeCurrentTransformMatrixLocked(); + + /** + * The default consumer usage flags that SurfaceTexture always sets on its + * BufferQueue instance; these will be OR:d with any additional flags passed + * from the SurfaceTexture user. In particular, SurfaceTexture will always + * consume buffers as hardware textures. + */ + static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; + + /** + * mCurrentCrop is the crop rectangle that applies to the current texture. + * It gets set each time updateTexImage is called. + */ + Rect mCurrentCrop; + + /** + * mCurrentTransform is the transform identifier for the current texture. It + * gets set each time updateTexImage is called. + */ + uint32_t mCurrentTransform; + + /** + * mCurrentScalingMode is the scaling mode for the current texture. It gets + * set each time updateTexImage is called. + */ + uint32_t mCurrentScalingMode; + + /** + * mCurrentFence is the fence received from BufferQueue in updateTexImage. + */ + sp<Fence> mCurrentFence; + + /** + * The FenceTime wrapper around mCurrentFence. + */ + std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE}; + + /** + * mCurrentTransformMatrix is the transform matrix for the current texture. + * It gets computed by computeTransformMatrix each time updateTexImage is + * called. + */ + float mCurrentTransformMatrix[16]; + + /** + * mCurrentTimestamp is the timestamp for the current texture. It + * gets set each time updateTexImage is called. + */ + int64_t mCurrentTimestamp; + + /** + * mCurrentDataSpace is the dataspace for the current texture. It + * gets set each time updateTexImage is called. + */ + android_dataspace mCurrentDataSpace; + + /** + * mCurrentFrameNumber is the frame counter for the current texture. + * It gets set each time updateTexImage is called. + */ + uint64_t mCurrentFrameNumber; + + uint32_t mDefaultWidth, mDefaultHeight; + + /** + * mFilteringEnabled indicates whether the transform matrix is computed for + * use with bilinear filtering. It defaults to true and is changed by + * setFilteringEnabled(). + */ + bool mFilteringEnabled; + + /** + * mTexName is the name of the OpenGL texture to which streamed images will + * be bound when updateTexImage is called. It is set at construction time + * and can be changed with a call to attachToContext. + */ + uint32_t mTexName; + + /** + * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + * extension should be used to prevent buffers from being dequeued before + * it's safe for them to be written. It gets set at construction time and + * never changes. + */ + const bool mUseFenceSync; + + /** + * mTexTarget is the GL texture target with which the GL texture object is + * associated. It is set in the constructor and never changed. It is + * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android + * Browser. In that case it is set to GL_TEXTURE_2D to allow + * glCopyTexSubImage to read from the texture. This is a hack to work + * around a GL driver limitation on the number of FBO attachments, which the + * browser's tile cache exceeds. + */ + const uint32_t mTexTarget; + + /** + * mCurrentTexture is the buffer slot index of the buffer that is currently + * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + * indicating that no buffer slot is currently bound to the texture. Note, + * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + * that no buffer is bound to the texture. A call to setBufferCount will + * reset mCurrentTexture to INVALID_BUFFER_SLOT. + */ + int mCurrentTexture; + + enum class OpMode { detached, attachedToView, attachedToGL }; + /** + * mOpMode indicates whether the SurfaceTexture is currently attached to + * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to, + * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to + * whatever GL context is current at the time of the first updateTexImage call. + * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by + * attachToContext. + * attachToView/detachFromView are used to attach/detach from HWUI view system. + */ + OpMode mOpMode; + + /** + * mEGLConsumer has SurfaceTexture logic used when attached to GL context. + */ + EGLConsumer mEGLConsumer; + + /** + * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system. + */ + ImageConsumer mImageConsumer; + + friend class ImageConsumer; + friend class EGLConsumer; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp index 5b361548eeda..d2d37dcb34f2 100644 --- a/libs/hwui/tests/common/LeakChecker.cpp +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -16,7 +16,6 @@ #include "LeakChecker.h" -#include "Caches.h" #include "TestUtils.h" #include <memunreachable/memunreachable.h> @@ -71,9 +70,6 @@ void LeakChecker::checkForLeaks() { // thread-local caches so some leaks will not be properly tagged as leaks UnreachableMemoryInfo rtMemInfo; TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { - if (Caches::hasInstance()) { - Caches::getInstance().tasks.stop(); - } // Check for leaks if (!GetUnreachableMemory(rtMemInfo)) { cerr << "Failed to get unreachable memory!" << endl; diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 69586345319e..66b9b85bdbe7 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -67,16 +67,14 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater( renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, const SkMatrix& transform) { sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread); - layerUpdater->backingLayer()->getTransform().load(transform); + layerUpdater->backingLayer()->getTransform() = transform; layerUpdater->setSize(width, height); layerUpdater->setTransform(&transform); // updateLayer so it's ready to draw - layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - static_cast<GlLayer*>(layerUpdater->backingLayer()) - ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix identity; + identity.setIdentity(); + layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr); return layerUpdater; } @@ -117,7 +115,6 @@ void TestUtils::TestTask::run() { if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { renderThread.vulkanManager().destroy(); } else { - renderThread.renderState().flush(Caches::FlushMode::Full); renderThread.destroyGlContext(); } } diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 743f8093bfa8..0e6582c59a36 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -18,7 +18,6 @@ #include <DeviceInfo.h> #include <DisplayList.h> -#include <GlLayer.h> #include <Matrix.h> #include <Properties.h> #include <Rect.h> diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp index f29830f0e34b..6c8775b1bdbb 100644 --- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp +++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp @@ -15,12 +15,13 @@ */ #include "DeferredLayerUpdater.h" -#include "GlLayer.h" #include "Properties.h" #include "tests/common/TestUtils.h" #include <gtest/gtest.h> +#include <SkBitmap.h> +#include <SkImage.h> using namespace android; using namespace android::uirenderer; @@ -31,10 +32,6 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { layerUpdater->setBlend(true); // updates are deferred so the backing layer should still be in its default state - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget()); - } EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight()); EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter()); @@ -42,19 +39,13 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) { EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform()); // push the deferred updates to the layer - Matrix4 scaledMatrix; - scaledMatrix.loadScale(0.5, 0.5, 0.0); - layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN); - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); - glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES); - } + SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5); + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap); + layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage); // the backing layer should now have all the properties applied. - if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) { - GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer()); - EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget()); - } EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth()); EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight()); EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter()); diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 9e6d9a8c27de..aecceb3609f5 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -17,12 +17,13 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "Caches.h" #include "debug/GlesDriver.h" #include "debug/NullGlesDriver.h" #include "hwui/Typeface.h" #include "Properties.h" #include "tests/common/LeakChecker.h" +#include "thread/TaskProcessor.h" +#include "thread/Task.h" #include "thread/TaskManager.h" #include <signal.h> diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index f8e8a0a18284..ebf2343c5518 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -16,6 +16,7 @@ #ifndef PAINT_UTILS_H #define PAINT_UTILS_H +#include <GLES2/gl2.h> #include <utils/Blur.h> #include <SkColorFilter.h> diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 67682a06cb6a..c5a6ec590d30 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -36,9 +36,8 @@ cc_library_shared { cflags: [ "-Wall", + "-Wextra", "-Werror", - "-Wunused", - "-Wunreachable-code", ], } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 4794f3da824c..eb3469e0b3f0 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -28,7 +28,6 @@ #include <utils/BitSet.h> #include <utils/RefBase.h> #include <utils/Looper.h> -#include <utils/String8.h> #include <gui/DisplayEventReceiver.h> namespace android { diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index 173cd507d943..eb2bc98ec9e9 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -23,14 +23,10 @@ #include <utils/String8.h> #include <gui/Surface.h> -// ToDo: Fix code to be warning free -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" #include <SkBitmap.h> #include <SkCanvas.h> #include <SkColor.h> #include <SkPaint.h> -#pragma GCC diagnostic pop #include <android/native_window.h> diff --git a/native/android/Android.bp b/native/android/Android.bp index 4fb5e748aaac..43847cc4ab06 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -64,6 +64,7 @@ cc_library_shared { "libsensor", "libandroid_runtime", "libnetd_client", + "libhwui", ], static_libs: [ diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp index b26688190ccd..ced2792775d4 100644 --- a/native/android/surface_texture.cpp +++ b/native/android/surface_texture.cpp @@ -21,15 +21,16 @@ #include <utils/Log.h> -#include <gui/GLConsumer.h> #include <gui/Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> +#include "surfacetexture/SurfaceTexture.h" + using namespace android; struct ASurfaceTexture { - sp<GLConsumer> consumer; + sp<SurfaceTexture> consumer; sp<IGraphicBufferProducer> producer; }; diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 663b1f5e4789..240a19279739 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -675,6 +675,7 @@ public class ApplicationsState { if (!mResumed) { mResumed = true; mSessionsChanged = true; + doPauseLocked(); doResumeIfNeededLocked(); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 36d209e96269..7000f9d7a7d2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -97,8 +97,6 @@ public class LocalBluetoothProfileManager { private PanProfile mPanProfile; private PbapClientProfile mPbapClientProfile; private PbapServerProfile mPbapProfile; - private final boolean mUsePbapPce; - private final boolean mUseMapClient; private HearingAidProfile mHearingAidProfile; /** @@ -115,9 +113,6 @@ public class LocalBluetoothProfileManager { mDeviceManager = deviceManager; mEventManager = eventManager; - mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile); - // MAP Client is typically used in the same situations as PBAP Client - mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile); // pass this reference to adapter and event manager (circular dependency) adapter.setProfileManager(this); @@ -130,17 +125,17 @@ public class LocalBluetoothProfileManager { void updateLocalProfiles() { List<Integer> supportedList = BluetoothAdapter.getDefaultAdapter().getSupportedProfiles(); if (CollectionUtils.isEmpty(supportedList)) { - if(DEBUG) Log.d(TAG, "supportedList is null"); + if (DEBUG) Log.d(TAG, "supportedList is null"); return; } if (mA2dpProfile == null && supportedList.contains(BluetoothProfile.A2DP)) { - if(DEBUG) Log.d(TAG, "Adding local A2DP profile"); + if (DEBUG) Log.d(TAG, "Adding local A2DP profile"); mA2dpProfile = new A2dpProfile(mContext, mDeviceManager, this); addProfile(mA2dpProfile, A2dpProfile.NAME, BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); } if (mA2dpSinkProfile == null && supportedList.contains(BluetoothProfile.A2DP_SINK)) { - if(DEBUG) Log.d(TAG, "Adding local A2DP SINK profile"); + if (DEBUG) Log.d(TAG, "Adding local A2DP SINK profile"); mA2dpSinkProfile = new A2dpSinkProfile(mContext, mDeviceManager, this); addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME, BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); @@ -154,66 +149,63 @@ public class LocalBluetoothProfileManager { BluetoothHeadset.STATE_AUDIO_DISCONNECTED); } if (mHfpClientProfile == null && supportedList.contains(BluetoothProfile.HEADSET_CLIENT)) { - if(DEBUG) Log.d(TAG, "Adding local HfpClient profile"); + if (DEBUG) Log.d(TAG, "Adding local HfpClient profile"); mHfpClientProfile = new HfpClientProfile(mContext, mDeviceManager, this); addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME, BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED, BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); } - if (mUseMapClient) { - if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) { - if(DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile"); - mMapClientProfile = - new MapClientProfile(mContext, mDeviceManager,this); - addProfile(mMapClientProfile, MapClientProfile.NAME, - BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED); - } - } else if (mMapProfile == null && supportedList.contains(BluetoothProfile.MAP)) { - if(DEBUG) Log.d(TAG, "Adding local MAP profile"); + if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) { + if (DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile"); + mMapClientProfile = new MapClientProfile(mContext, mDeviceManager,this); + addProfile(mMapClientProfile, MapClientProfile.NAME, + BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED); + } + if (mMapProfile == null && supportedList.contains(BluetoothProfile.MAP)) { + if (DEBUG) Log.d(TAG, "Adding local MAP profile"); mMapProfile = new MapProfile(mContext, mDeviceManager, this); addProfile(mMapProfile, MapProfile.NAME, BluetoothMap.ACTION_CONNECTION_STATE_CHANGED); } if (mOppProfile == null && supportedList.contains(BluetoothProfile.OPP)) { - if(DEBUG) Log.d(TAG, "Adding local OPP profile"); + if (DEBUG) Log.d(TAG, "Adding local OPP profile"); mOppProfile = new OppProfile(); // Note: no event handler for OPP, only name map. mProfileNameMap.put(OppProfile.NAME, mOppProfile); } if (mHearingAidProfile == null && supportedList.contains(BluetoothProfile.HEARING_AID)) { - if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile"); + if (DEBUG) Log.d(TAG, "Adding local Hearing Aid profile"); mHearingAidProfile = new HearingAidProfile(mContext, mDeviceManager, this); addProfile(mHearingAidProfile, HearingAidProfile.NAME, BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); } if (mHidProfile == null && supportedList.contains(BluetoothProfile.HID_HOST)) { - if(DEBUG) Log.d(TAG, "Adding local HID_HOST profile"); + if (DEBUG) Log.d(TAG, "Adding local HID_HOST profile"); mHidProfile = new HidProfile(mContext, mDeviceManager, this); addProfile(mHidProfile, HidProfile.NAME, BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED); } if (mHidDeviceProfile == null && supportedList.contains(BluetoothProfile.HID_DEVICE)) { - if(DEBUG) Log.d(TAG, "Adding local HID_DEVICE profile"); + if (DEBUG) Log.d(TAG, "Adding local HID_DEVICE profile"); mHidDeviceProfile = new HidDeviceProfile(mContext, mDeviceManager, this); addProfile(mHidDeviceProfile, HidDeviceProfile.NAME, BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED); } if (mPanProfile == null && supportedList.contains(BluetoothProfile.PAN)) { - if(DEBUG) Log.d(TAG, "Adding local PAN profile"); + if (DEBUG) Log.d(TAG, "Adding local PAN profile"); mPanProfile = new PanProfile(mContext); addPanProfile(mPanProfile, PanProfile.NAME, BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); } if (mPbapProfile == null && supportedList.contains(BluetoothProfile.PBAP)) { - if(DEBUG) Log.d(TAG, "Adding local PBAP profile"); + if (DEBUG) Log.d(TAG, "Adding local PBAP profile"); mPbapProfile = new PbapServerProfile(mContext); addProfile(mPbapProfile, PbapServerProfile.NAME, BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED); } - if (mUsePbapPce && mPbapClientProfile == null && supportedList.contains( - BluetoothProfile.PBAP_CLIENT)) { - if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile"); + if (mPbapClientProfile == null && supportedList.contains(BluetoothProfile.PBAP_CLIENT)) { + if (DEBUG) Log.d(TAG, "Adding local PBAP Client profile"); mPbapClientProfile = new PbapClientProfile(mContext, mDeviceManager,this); addProfile(mPbapClientProfile, PbapClientProfile.NAME, BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED); @@ -544,7 +536,9 @@ public class LocalBluetoothProfileManager { removedProfiles.remove(mMapClientProfile); } - if (mUsePbapPce) { + if ((mPbapClientProfile != null) && + BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.PBAP_PCE) && + BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) { profiles.add(mPbapClientProfile); removedProfiles.remove(mPbapClientProfile); } diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java index 88be2b0a0bd2..3e3c03918a0b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java @@ -27,7 +27,6 @@ import android.icu.util.ULocale; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.TtsSpan; -import android.util.Log; import com.android.settingslib.R; @@ -37,8 +36,6 @@ import java.util.Locale; /** Utility class for generally useful string methods **/ public class StringUtil { - private static final String TAG = "StringUtil"; - public static final int SECONDS_PER_MINUTE = 60; public static final int SECONDS_PER_HOUR = 60 * 60; public static final int SECONDS_PER_DAY = 24 * 60 * 60; @@ -97,7 +94,6 @@ public class StringUtil { final Locale locale = context.getResources().getConfiguration().locale; final MeasureFormat measureFormat = MeasureFormat.getInstance( locale, FormatWidth.SHORT); - Log.i(TAG, "Locale is: " + locale); sb.append(measureFormat.formatMeasures(measureArray)); if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) { @@ -150,7 +146,6 @@ public class StringUtil { null /* default NumberFormat */, RelativeDateTimeFormatter.Style.LONG, android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE); - Log.i(TAG, "Locale is: " + locale); return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit); } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java index c268d325bd02..0cdb509a5209 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java @@ -29,7 +29,7 @@ public abstract class QSIconView extends ViewGroup { super(context); } - public abstract void setIcon(State state); + public abstract void setIcon(State state, boolean allowAnimations); public abstract void disableAnimation(); public abstract View getIconView(); } diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d051f07e44a8..26eadb5bfa2d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -361,7 +361,7 @@ <dimen name="qs_quick_layout_width">-1px</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> <dimen name="qs_header_gear_translation">16dp</dimen> - <dimen name="qs_header_tile_margin_horizontal">0dp</dimen> + <dimen name="qs_header_tile_margin_horizontal">4dp</dimen> <dimen name="qs_page_indicator_width">16dp</dimen> <dimen name="qs_page_indicator_height">8dp</dimen> <dimen name="qs_tile_icon_size">24dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java index e7eefe8d5e56..376e6ae16cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java @@ -43,9 +43,9 @@ public class CellTileView extends SignalTileView { R.dimen.qs_tile_icon_size)); } - protected void updateIcon(ImageView iv, State state) { + protected void updateIcon(ImageView iv, State state, boolean allowAnimations) { if (!(state.icon instanceof SignalIcon)) { - super.updateIcon(iv, state); + super.updateIcon(iv, state, allowAnimations); return; } else if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) { mSignalDrawable.setLevel(((SignalIcon) state.icon).getState()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java index d9583af65df6..ce90fc107b82 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java @@ -18,14 +18,13 @@ package com.android.systemui.qs; import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.drawable.Drawable; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.systemui.R; -import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; +import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.tileimpl.QSIconViewImpl; import com.android.systemui.qs.tileimpl.SlashImageView; @@ -119,9 +118,9 @@ public class SignalTileView extends QSIconViewImpl { } @Override - public void setIcon(QSTile.State state) { + public void setIcon(State state, boolean allowAnimations) { final SignalState s = (SignalState) state; - setIcon(mSignal, s); + setIcon(mSignal, s, allowAnimations); if (s.overlayIconId > 0) { mOverlay.setVisibility(VISIBLE); @@ -134,9 +133,9 @@ public class SignalTileView extends QSIconViewImpl { } else { mSignal.setPaddingRelative(0, 0, 0, 0); } - final boolean shown = isShown(); - setVisibility(mIn, shown, s.activityIn); - setVisibility(mOut, shown, s.activityOut); + final boolean shouldAnimate = allowAnimations && isShown(); + setVisibility(mIn, shouldAnimate, s.activityIn); + setVisibility(mOut, shouldAnimate, s.activityOut); } private void setVisibility(View view, boolean shown, boolean visible) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index e7e756f87c84..9dd5d8fbc776 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -84,16 +84,15 @@ public class QSIconViewImpl extends QSIconView { layout(mIcon, iconLeft, top); } - public void setIcon(QSTile.State state) { - setIcon((ImageView) mIcon, state); + public void setIcon(State state, boolean allowAnimations) { + setIcon((ImageView) mIcon, state, allowAnimations); } - protected void updateIcon(ImageView iv, State state) { + protected void updateIcon(ImageView iv, State state, boolean allowAnimations) { final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon; if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag)) || !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) { - boolean shouldAnimate = iv.isShown() && mAnimationEnabled - && iv.getDrawable() != null; + boolean shouldAnimate = allowAnimations && shouldAnimate(iv); Drawable d = icon != null ? shouldAnimate ? icon.getDrawable(mContext) : icon.getInvisibleDrawable(mContext) : null; @@ -128,7 +127,11 @@ public class QSIconViewImpl extends QSIconView { } } - protected void setIcon(ImageView iv, QSTile.State state) { + private boolean shouldAnimate(ImageView iv) { + return mAnimationEnabled && iv.isShown() && iv.getDrawable() != null; + } + + protected void setIcon(ImageView iv, QSTile.State state, boolean allowAnimations) { if (state.disabledByPolicy) { iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color)); } else { @@ -137,8 +140,8 @@ public class QSIconViewImpl extends QSIconView { if (state.state != mState) { int color = getColor(state.state); mState = state.state; - if (iv.isShown() && mTint != 0) { - animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state)); + if (mTint != 0 && allowAnimations && shouldAnimate(iv)) { + animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations)); mTint = color; } else { if (iv instanceof AlphaControlledSlashImageView) { @@ -148,10 +151,10 @@ public class QSIconViewImpl extends QSIconView { setTint(iv, color); } mTint = color; - updateIcon(iv, state); + updateIcon(iv, state, allowAnimations); } } else { - updateIcon(iv, state); + updateIcon(iv, state, allowAnimations); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index 91afef02a246..d42127e74944 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -47,6 +47,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { private static final String TAG = "QSTileBaseView"; private final H mHandler = new H(); + private final int[] mLocInScreen = new int[2]; private final FrameLayout mIconFrame; protected QSIconView mIcon; protected RippleDrawable mRipple; @@ -178,8 +179,9 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { protected void handleStateChanged(QSTile.State state) { int circleColor = getCircleColor(state.state); + boolean allowAnimations = animationsEnabled(); if (circleColor != mCircleColor) { - if (mBg.isShown() && animationsEnabled()) { + if (allowAnimations) { ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor) .setDuration(QS_ANIM_LENGTH); animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf( @@ -192,7 +194,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { } setClickable(state.state != Tile.STATE_UNAVAILABLE); - mIcon.setIcon(state); + mIcon.setIcon(state, allowAnimations); setContentDescription(state.contentDescription); mAccessibilityClass = state.expandedAccessibilityClassName; @@ -205,8 +207,17 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { } } + /* The view should not be animated if it's not on screen and no part of it is visible. + */ protected boolean animationsEnabled() { - return true; + if (!isShown()) { + return false; + } + if (getAlpha() != 1f) { + return false; + } + getLocationOnScreen(mLocInScreen); + return mLocInScreen[1] >= -getHeight(); } private int getCircleColor(int state) { diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java index 238407a9a6f1..a46f018af816 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java @@ -16,17 +16,13 @@ package com.android.systemui.usb; -import android.annotation.NonNull; import android.app.AlertDialog; import android.app.PendingIntent; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.res.XmlResourceParser; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; @@ -45,13 +41,8 @@ import android.widget.TextView; import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; -import com.android.internal.util.XmlUtils; -import android.hardware.usb.AccessoryFilter; -import android.hardware.usb.DeviceFilter; import com.android.systemui.R; -import org.xmlpull.v1.XmlPullParser; - public class UsbPermissionActivity extends AlertActivity implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener { @@ -71,12 +62,13 @@ public class UsbPermissionActivity extends AlertActivity public void onCreate(Bundle icicle) { super.onCreate(icicle); - Intent intent = getIntent(); + Intent intent = getIntent(); mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT); mUid = intent.getIntExtra(Intent.EXTRA_UID, -1); - mPackageName = intent.getStringExtra("package"); + mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE); + boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false); PackageManager packageManager = getPackageManager(); ApplicationInfo aInfo; @@ -105,121 +97,27 @@ public class UsbPermissionActivity extends AlertActivity ap.mPositiveButtonListener = this; ap.mNegativeButtonListener = this; - try { - PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName, - PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); - - if ((mDevice != null && canBeDefault(mDevice, packageInfo)) - || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) { - // add "open when" checkbox - LayoutInflater inflater = (LayoutInflater) getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); - mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse); - if (mDevice == null) { - mAlwaysUse.setText(getString(R.string.always_use_accessory, appName, - mAccessory.getDescription())); - } else { - mAlwaysUse.setText(getString(R.string.always_use_device, appName, - mDevice.getProductName())); - } - mAlwaysUse.setOnCheckedChangeListener(this); - - mClearDefaultHint = (TextView)ap.mView.findViewById( - com.android.internal.R.id.clearDefaultHint); - mClearDefaultHint.setVisibility(View.GONE); + if (canBeDefault && (mDevice != null || mAccessory != null)) { + // add "open when" checkbox + LayoutInflater inflater = (LayoutInflater) getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null); + mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse); + if (mDevice == null) { + mAlwaysUse.setText(getString(R.string.always_use_accessory, appName, + mAccessory.getDescription())); + } else { + mAlwaysUse.setText(getString(R.string.always_use_device, appName, + mDevice.getProductName())); } - } catch (PackageManager.NameNotFoundException e) { - // ignore - } - - setupAlert(); - - } - - /** - * Can the app be the default for the USB device. I.e. can the app be launched by default if - * the device is plugged in. - * - * @param device The device the app would be default for - * @param packageInfo The package info of the app - * - * @return {@code true} iff the app can be default - */ - private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) { - ActivityInfo[] activities = packageInfo.activities; - if (activities != null) { - int numActivities = activities.length; - for (int i = 0; i < numActivities; i++) { - ActivityInfo activityInfo = activities[i]; + mAlwaysUse.setOnCheckedChangeListener(this); - try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(), - UsbManager.ACTION_USB_DEVICE_ATTACHED)) { - if (parser == null) { - continue; - } - - XmlUtils.nextElement(parser); - while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { - if ("usb-device".equals(parser.getName())) { - DeviceFilter filter = DeviceFilter.read(parser); - if (filter.matches(device)) { - return true; - } - } - - XmlUtils.nextElement(parser); - } - } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e); - } - } - } - - return false; - } - - /** - * Can the app be the default for the USB accessory. I.e. can the app be launched by default if - * the accessory is plugged in. - * - * @param accessory The accessory the app would be default for - * @param packageInfo The package info of the app - * - * @return {@code true} iff the app can be default - */ - private boolean canBeDefault(@NonNull UsbAccessory accessory, - @NonNull PackageInfo packageInfo) { - ActivityInfo[] activities = packageInfo.activities; - if (activities != null) { - int numActivities = activities.length; - for (int i = 0; i < numActivities; i++) { - ActivityInfo activityInfo = activities[i]; - - try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(), - UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { - if (parser == null) { - continue; - } - - XmlUtils.nextElement(parser); - while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { - if ("usb-accessory".equals(parser.getName())) { - AccessoryFilter filter = AccessoryFilter.read(parser); - if (filter.matches(accessory)) { - return true; - } - } - - XmlUtils.nextElement(parser); - } - } catch (Exception e) { - Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e); - } - } + mClearDefaultHint = (TextView)ap.mView.findViewById( + com.android.internal.R.id.clearDefaultHint); + mClearDefaultHint.setVisibility(View.GONE); } - return false; + setupAlert(); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java index f9f4f497a2ec..c5e404385f8b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java @@ -59,14 +59,14 @@ public class QSIconViewImplTest extends SysuiTestCase { // No current icon, only the static drawable should be used. s.icon = mock(Icon.class); when(iv.getDrawable()).thenReturn(null); - mIconView.updateIcon(iv, s); + mIconView.updateIcon(iv, s, true); verify(s.icon, never()).getDrawable(any()); verify(s.icon).getInvisibleDrawable(any()); // Has icon, should use the standard (animated) form. s.icon = mock(Icon.class); when(iv.getDrawable()).thenReturn(mock(Drawable.class)); - mIconView.updateIcon(iv, s); + mIconView.updateIcon(iv, s, true); verify(s.icon).getDrawable(any()); verify(s.icon, never()).getInvisibleDrawable(any()); } @@ -79,7 +79,7 @@ public class QSIconViewImplTest extends SysuiTestCase { int desiredColor = mIconView.getColor(s.state); when(iv.isShown()).thenReturn(true); - mIconView.setIcon(iv, s); + mIconView.setIcon(iv, s, true); verify(iv).setImageTintList(argThat(stateList -> stateList.getColors()[0] == desiredColor)); } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ba5f32308a30..760209024c57 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -183,6 +183,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -202,6 +203,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final String DIAG_ARG = "--diag"; public static final String SHORT_ARG = "--short"; private static final String TETHERING_ARG = "tethering"; + private static final String NETWORK_ARG = "networks"; + private static final String REQUEST_ARG = "requests"; private static final boolean DBG = true; private static final boolean VDBG = false; @@ -1978,7 +1981,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void dumpNetworkDiagnostics(IndentingPrintWriter pw) { final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>(); final long DIAG_TIME_MS = 5000; - for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + for (NetworkAgentInfo nai : networksSortedById()) { // Start gathering diagnostic information. netDiags.add(new NetworkDiagnostics( nai.network, @@ -2009,6 +2012,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } else if (ArrayUtils.contains(args, TETHERING_ARG)) { mTethering.dump(fd, pw, args); return; + } else if (ArrayUtils.contains(args, NETWORK_ARG)) { + dumpNetworks(pw); + return; + } else if (ArrayUtils.contains(args, REQUEST_ARG)) { + dumpNetworkRequests(pw); + return; } pw.print("NetworkFactories for:"); @@ -2029,36 +2038,15 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println("Current Networks:"); pw.increaseIndent(); - for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { - pw.println(nai.toString()); - pw.increaseIndent(); - pw.println(String.format( - "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d", - nai.numForegroundNetworkRequests(), - nai.numNetworkRequests() - nai.numRequestNetworkRequests(), - nai.numBackgroundNetworkRequests(), - nai.numNetworkRequests())); - pw.increaseIndent(); - for (int i = 0; i < nai.numNetworkRequests(); i++) { - pw.println(nai.requestAt(i).toString()); - } - pw.decreaseIndent(); - pw.println("Lingered:"); - pw.increaseIndent(); - nai.dumpLingerTimers(pw); - pw.decreaseIndent(); - pw.decreaseIndent(); - } + dumpNetworks(pw); pw.decreaseIndent(); pw.println(); pw.println("Network Requests:"); pw.increaseIndent(); - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - pw.println(nri.toString()); - } - pw.println(); + dumpNetworkRequests(pw); pw.decreaseIndent(); + pw.println(); mLegacyTypeTracker.dump(pw); @@ -2126,6 +2114,55 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void dumpNetworks(IndentingPrintWriter pw) { + for (NetworkAgentInfo nai : networksSortedById()) { + pw.println(nai.toString()); + pw.increaseIndent(); + pw.println(String.format( + "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d", + nai.numForegroundNetworkRequests(), + nai.numNetworkRequests() - nai.numRequestNetworkRequests(), + nai.numBackgroundNetworkRequests(), + nai.numNetworkRequests())); + pw.increaseIndent(); + for (int i = 0; i < nai.numNetworkRequests(); i++) { + pw.println(nai.requestAt(i).toString()); + } + pw.decreaseIndent(); + pw.println("Lingered:"); + pw.increaseIndent(); + nai.dumpLingerTimers(pw); + pw.decreaseIndent(); + pw.decreaseIndent(); + } + } + + private void dumpNetworkRequests(IndentingPrintWriter pw) { + for (NetworkRequestInfo nri : requestsSortedById()) { + pw.println(nri.toString()); + } + } + + /** + * Return an array of all current NetworkAgentInfos sorted by network id. + */ + private NetworkAgentInfo[] networksSortedById() { + NetworkAgentInfo[] networks = new NetworkAgentInfo[0]; + networks = mNetworkAgentInfos.values().toArray(networks); + Arrays.sort(networks, Comparator.comparingInt(nai -> nai.network.netId)); + return networks; + } + + /** + * Return an array of all current NetworkRequest sorted by request id. + */ + private NetworkRequestInfo[] requestsSortedById() { + NetworkRequestInfo[] requests = new NetworkRequestInfo[0]; + requests = mNetworkRequests.values().toArray(requests); + Arrays.sort(requests, Comparator.comparingInt(nri -> nri.request.requestId)); + return requests; + } + private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) { if (nai.network == null) return false; final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network); @@ -2916,7 +2953,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println("User setting: " + description); pw.println("Network overrides:"); pw.increaseIndent(); - for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { + for (NetworkAgentInfo nai : networksSortedById()) { if (nai.avoidUnvalidated) { pw.println(nai.name()); } diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 4d3468e21e75..44dddd14d83d 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -4386,12 +4386,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final class LocalServiceImpl extends InputMethodManagerInternal { @NonNull private final InputMethodManagerService mService; - @NonNull - private final Handler mHandler; LocalServiceImpl(@NonNull InputMethodManagerService service) { mService = service; - mHandler = service.mHandler; } @Override @@ -4411,19 +4408,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void setInteractive(boolean interactive) { // Do everything in handler so as not to block the caller. - mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE, - interactive ? 1 : 0, 0)); + mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0) + .sendToTarget(); } @Override public void hideCurrentInputMethod() { - mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); - mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD); + mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); + mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD); } @Override public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName)); + mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget(); } } diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java new file mode 100644 index 000000000000..237e169a989f --- /dev/null +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.os.Binder; +import android.os.Looper; +import android.os.ResultReceiver; +import android.os.ShellCallback; +import android.os.ShellCommand; + +import com.android.internal.os.LooperStats; +import com.android.internal.util.DumpUtils; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * @hide Only for use within the system server. + */ +public class LooperStatsService extends Binder { + private static final String TAG = "LooperStatsService"; + private static final String LOOPER_STATS_SERVICE_NAME = "looper_stats"; + + private final Context mContext; + private final LooperStats mStats; + private boolean mEnabled = false; + + private LooperStatsService(Context context, LooperStats stats) { + this.mContext = context; + this.mStats = stats; + } + + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + (new LooperShellCommand()).exec(this, in, out, err, args, callback, resultReceiver); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + List<LooperStats.ExportedEntry> entries = mStats.getEntries(); + entries.sort(Comparator + .comparing((LooperStats.ExportedEntry entry) -> entry.threadName) + .thenComparing(entry -> entry.handlerClassName) + .thenComparing(entry -> entry.messageName)); + String header = String.join(",", Arrays.asList( + "thread_name", + "handler_class", + "message_name", + "message_count", + "recorded_message_count", + "total_latency_micros", + "max_latency_micros", + "total_cpu_micros", + "max_cpu_micros", + "exception_count")); + pw.println(header); + for (LooperStats.ExportedEntry entry : entries) { + pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName, entry.handlerClassName, + entry.messageName, entry.messageCount, entry.recordedMessageCount, + entry.totalLatencyMicros, entry.maxLatencyMicros, entry.cpuUsageMicros, + entry.maxCpuUsageMicros, entry.exceptionCount); + } + } + + private void setEnabled(boolean enabled) { + if (mEnabled != enabled) { + mEnabled = enabled; + mStats.reset(); + Looper.setObserver(enabled ? mStats : null); + } + } + + /** + * Manages the lifecycle of LooperStatsService within System Server. + */ + public static class Lifecycle extends SystemService { + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + LooperStats stats = new LooperStats(); + publishLocalService(LooperStats.class, stats); + // TODO: publish LooperStatsService as a binder service when the SE Policy is changed. + } + } + + private class LooperShellCommand extends ShellCommand { + @Override + public int onCommand(String cmd) { + if ("enable".equals(cmd)) { + setEnabled(true); + return 0; + } else if ("disable".equals(cmd)) { + setEnabled(false); + return 0; + } else if ("reset".equals(cmd)) { + mStats.reset(); + return 0; + } else { + return handleDefaultCommands(cmd); + } + } + + @Override + public void onHelp() { + final PrintWriter pw = getOutPrintWriter(); + pw.println(LOOPER_STATS_SERVICE_NAME + " commands:"); + pw.println(" enable: Enable collecting stats"); + pw.println(" disable: Disable collecting stats"); + pw.println(" reset: Reset stats"); + } + } +} diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index c473ef23db3e..019d726d8c48 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -51,6 +51,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ColorDisplayController; import com.android.internal.os.BackgroundThread; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.RingBuffer; @@ -72,7 +73,6 @@ import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; import java.util.Deque; import java.util.HashMap; @@ -363,12 +363,9 @@ public class BrightnessTracker { return; } - builder.setNightMode(mInjector.getSecureIntForUser(mContentResolver, - Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT) - == 1); - builder.setColorTemperature(mInjector.getSecureIntForUser(mContentResolver, - Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, - 0, UserHandle.USER_CURRENT)); + builder.setNightMode(mInjector.isNightModeActive(mContext, UserHandle.USER_CURRENT)); + builder.setColorTemperature(mInjector.getColorTemperature(mContext, + UserHandle.USER_CURRENT)); BrightnessChangeEvent event = builder.build(); if (DEBUG) { @@ -952,5 +949,13 @@ public class BrightnessTracker { public boolean isInteractive(Context context) { return context.getSystemService(PowerManager.class).isInteractive(); } + + public int getColorTemperature(Context context, int userId) { + return new ColorDisplayController(context, userId).getColorTemperature(); + } + + public boolean isNightModeActive(Context context, int userId) { + return new ColorDisplayController(context, userId).isActivated(); + } } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 9df9ba600f2a..2750dc13b408 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -16,6 +16,9 @@ package com.android.server.input; +import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; +import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; + import android.annotation.NonNull; import android.os.LocaleList; import android.os.ShellCallback; @@ -292,11 +295,6 @@ public class InputManagerService extends IInputManager.Stub /** Switch code: Camera lens cover. When set the lens is covered. */ public static final int SW_CAMERA_LENS_COVER = 0x09; - // Viewport constants defined in InputReader.h. - public static final int VIEWPORT_DEFAULT = 1; - public static final int VIEWPORT_EXTERNAL = 2; - public static final int VIEWPORT_VIRTUAL = 3; - public static final int SW_LID_BIT = 1 << SW_LID; public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE; public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE; @@ -362,7 +360,7 @@ public class InputManagerService extends IInputManager.Stub updateAccessibilityLargePointerFromSettings(); } - // TODO(BT) Pass in paramter for bluetooth system + // TODO(BT) Pass in parameter for bluetooth system public void systemRunning() { if (DEBUG) { Slog.d(TAG, "System ready."); @@ -417,7 +415,7 @@ public class InputManagerService extends IInputManager.Stub DisplayViewport externalTouchViewport, List<DisplayViewport> virtualTouchViewports) { if (defaultViewport.valid) { - setDisplayViewport(VIEWPORT_DEFAULT, defaultViewport); + setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport); } if (externalTouchViewport.valid) { diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index a54811b1a069..31cf9e346195 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -69,7 +69,6 @@ import android.os.WorkSource.WorkChain; import android.provider.Settings; import android.provider.Telephony.Carriers; import android.telephony.CarrierConfigManager; -import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; @@ -1577,6 +1576,8 @@ public class GnssLocationProvider implements LocationProviderInterface, InjectNt mSingleShot = false; native_stop(); mLastFixTime = 0; + // native_stop() may reset the position mode in hardware. + mLastPositionMode = null; // reset SV count to zero updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 91d4717ac23a..bdf12cad9cc1 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -334,9 +334,6 @@ public final class PowerManagerService extends SystemService // True if boot completed occurred. We keep the screen on until this happens. private boolean mBootCompleted; - // Runnables that should be triggered on boot completed - private Runnable[] mBootCompletedRunnables; - // True if auto-suspend mode is enabled. // Refer to autosuspend.h. private boolean mHalAutoSuspendModeEnabled; @@ -732,14 +729,6 @@ public final class PowerManagerService extends SystemService userActivityNoUpdateLocked( now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); updatePowerStateLocked(); - - if (!ArrayUtils.isEmpty(mBootCompletedRunnables)) { - Slog.d(TAG, "Posting " + mBootCompletedRunnables.length + " delayed runnables"); - for (Runnable r : mBootCompletedRunnables) { - BackgroundThread.getHandler().post(r); - } - } - mBootCompletedRunnables = null; } } } @@ -954,16 +943,6 @@ public final class PowerManagerService extends SystemService mDirty |= DIRTY_SETTINGS; } - private void postAfterBootCompleted(Runnable r) { - if (mBootCompleted) { - BackgroundThread.getHandler().post(r); - } else { - Slog.d(TAG, "Delaying runnable until system is booted"); - mBootCompletedRunnables = ArrayUtils.appendElement(Runnable.class, - mBootCompletedRunnables, r); - } - } - private void handleSettingsChangedLocked() { updateSettingsLocked(); updatePowerStateLocked(); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 41c0be63a5bb..b922e40a5d38 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -70,6 +70,7 @@ import com.android.internal.os.KernelUidCpuFreqTimeReader; import com.android.internal.os.KernelUidCpuTimeReader; import com.android.internal.os.KernelWakelockReader; import com.android.internal.os.KernelWakelockStats; +import com.android.internal.os.LooperStats; import com.android.internal.os.PowerProfile; import com.android.internal.util.DumpUtils; import com.android.server.BinderCallsStatsService; @@ -938,6 +939,29 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) { + LooperStats looperStats = LocalServices.getService(LooperStats.class); + if (looperStats == null) { + return; + } + + List<LooperStats.ExportedEntry> entries = looperStats.getEntries(); + long elapsedNanos = SystemClock.elapsedRealtimeNanos(); + for (LooperStats.ExportedEntry entry : entries) { + StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */); + e.writeLong(0); // uid collection not implemented yet + e.writeString(entry.handlerClassName); + e.writeString(entry.threadName); + e.writeString(entry.messageName); + e.writeLong(entry.messageCount); + e.writeLong(entry.exceptionCount); + e.writeLong(entry.recordedMessageCount); + e.writeLong(entry.totalLatencyMicros); + e.writeLong(entry.cpuUsageMicros); + pulledData.add(e); + } + } + /** * Pulls various data. */ @@ -1028,6 +1052,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullBinderCallsStatsExceptions(tagId, ret); break; } + case StatsLog.LOOPER_STATS: { + pullLooperStats(tagId, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 32fa9bf97930..9490ae2a894a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1426,7 +1426,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo config.densityDpi = displayInfo.logicalDensityDpi; config.colorMode = - (displayInfo.isHdr() + ((displayInfo.isHdr() && mService.hasHdrSupport()) ? Configuration.COLOR_MODE_HDR_YES : Configuration.COLOR_MODE_HDR_NO) | (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport() diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 71f34c98ee1f..eb419c9684f5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -22,10 +22,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRA import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.res.Configuration.EMPTY; + import static com.android.server.EventLogTags.WM_TASK_REMOVED; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS; import static com.android.server.wm.TaskProto.BOUNDS; import static com.android.server.wm.TaskProto.DEFER_REMOVAL; @@ -33,6 +31,9 @@ import static com.android.server.wm.TaskProto.FILLS_PARENT; import static com.android.server.wm.TaskProto.ID; import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS; import static com.android.server.wm.TaskProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.CallSuper; import android.app.ActivityManager.TaskDescription; @@ -43,8 +44,8 @@ import android.util.EventLog; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Surface; - import android.view.SurfaceControl; + import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; @@ -472,7 +473,8 @@ class Task extends WindowContainer<AppWindowToken> { void setDragResizing(boolean dragResizing, int dragResizeMode) { if (mDragResizing != dragResizing) { - if (!DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) { + // No need to check if the mode is allowed if it's leaving dragResize + if (dragResizing && !DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) { throw new IllegalArgumentException("Drag resize mode not allow for stack stackId=" + mStack.mStackId + " dragResizeMode=" + dragResizeMode); } diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index 5f41df72394a..228bfade25d5 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -30,18 +30,19 @@ import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME; import android.annotation.NonNull; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; - -import java.io.PrintWriter; import android.view.DisplayCutout; import com.android.server.wm.utils.WmDisplayCutout; +import java.io.PrintWriter; + /** * Container class for all the window frames that affect how windows are laid out. * * TODO(b/111611553): Investigate which frames are still needed and which are duplicates */ public class WindowFrames { + private static final StringBuilder sTmpSB = new StringBuilder(); /** * In most cases, this is the area of the entire screen. @@ -197,29 +198,18 @@ public class WindowFrames { } public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("Frames: containing="); - mContainingFrame.printShortString(pw); - pw.print(" parent="); mParentFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print(" display="); - mDisplayFrame.printShortString(pw); - pw.print(" overscan="); mOverscanFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print(" content="); - mContentFrame.printShortString(pw); - pw.print(" visible="); mVisibleFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print(" decor="); - mDecorFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print(" outset="); - mOutsetFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); - pw.print(" last="); mLastFrame.printShortString(pw); - pw.println(); - pw.print(prefix); pw.print(" cutout=" + mDisplayCutout.getDisplayCutout()); - pw.print(" last=" + mLastDisplayCutout.getDisplayCutout()); - pw.println(); + pw.println(prefix + "Frames: containing=" + + mContainingFrame.toShortString(sTmpSB) + + " parent=" + mParentFrame.toShortString(sTmpSB)); + pw.println(prefix + " display=" + mDisplayFrame.toShortString(sTmpSB) + + " overscan=" + mOverscanFrame.toShortString(sTmpSB)); + pw.println(prefix + " content=" + mContentFrame.toShortString(sTmpSB) + + " visible=" + mVisibleFrame.toShortString(sTmpSB)); + pw.println(prefix + " decor=" + mDecorFrame.toShortString(sTmpSB)); + pw.println(prefix + " outset=" + mOutsetFrame.toShortString(sTmpSB)); + pw.println(prefix + "mFrame=" + mFrame.toShortString(sTmpSB) + + " last=" + mLastFrame.toShortString(sTmpSB)); + pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout() + + " last=" + mLastDisplayCutout.getDisplayCutout()); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e80a47eef2d5..2a381aca8a2a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -734,8 +734,9 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; - // Indicates whether this device supports wide color gamut rendering + // Indicates whether this device supports wide color gamut / HDR rendering private boolean mHasWideColorGamutSupport; + private boolean mHasHdrSupport; // Who is holding the screen on. private Session mHoldingScreenOn; @@ -4504,6 +4505,7 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.systemReady(); mTaskSnapshotController.systemReady(); mHasWideColorGamutSupport = queryWideColorGamutSupport(); + mHasHdrSupport = queryHdrSupport(); } private static boolean queryWideColorGamutSupport() { @@ -4519,6 +4521,19 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + private static boolean queryHdrSupport() { + try { + ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); + OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay(); + if (hasHdr != null) { + return hasHdr.value; + } + } catch (RemoteException e) { + // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store + } + return false; + } + // ------------------------------------------------------------- // Async Handler // ------------------------------------------------------------- @@ -7499,6 +7514,10 @@ public class WindowManagerService extends IWindowManager.Stub SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1; } + boolean hasHdrSupport() { + return mHasHdrSupport && hasWideColorGamutSupport(); + } + void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) { if (!win.hideNonSystemOverlayWindowsWhenVisible() && !mHidingNonSystemOverlayWindows.contains(win)) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 97313f2891c3..466e298974d0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -602,6 +602,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private long mFrameNumber = -1; + private static final StringBuilder sTmpSB = new StringBuilder(); + /** * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms * of z-order and 1 otherwise. @@ -1113,9 +1115,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0)); } - mWindowFrames.setDisplayCutout( - windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame)); + windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame)); // Offset the actual frame by the amount layout frame is off. mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff); @@ -3336,183 +3337,160 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { final TaskStack stack = getStack(); - pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId()); - if (stack != null) { - pw.print(" stackId="); pw.print(stack.mStackId); - } - pw.print(" mSession="); pw.print(mSession); - pw.print(" mClient="); pw.println(mClient.asBinder()); - pw.print(prefix); pw.print("mOwnerUid="); pw.print(mOwnerUid); - pw.print(" mShowToOwnerOnly="); pw.print(mShowToOwnerOnly); - pw.print(" package="); pw.print(mAttrs.packageName); - pw.print(" appop="); pw.println(AppOpsManager.opToName(mAppOp)); - pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs.toString(prefix)); - pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); - pw.print(" h="); pw.print(mRequestedHeight); - pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + pw.print(prefix + "mDisplayId=" + getDisplayId()); + if (stack != null) { + pw.print(" stackId=" + stack.mStackId); + } + pw.println(" mSession=" + mSession + + " mClient=" + mClient.asBinder()); + pw.println(prefix + "mOwnerUid=" + mOwnerUid + + " mShowToOwnerOnly=" + mShowToOwnerOnly + + " package=" + mAttrs.packageName + + " appop=" + AppOpsManager.opToName(mAppOp)); + pw.println(prefix + "mAttrs=" + mAttrs.toString(prefix)); + pw.println(prefix + "Requested w=" + mRequestedWidth + + " h=" + mRequestedHeight + + " mLayoutSeq=" + mLayoutSeq); if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) { - pw.print(prefix); pw.print("LastRequested w="); pw.print(mLastRequestedWidth); - pw.print(" h="); pw.println(mLastRequestedHeight); + pw.println(prefix + "LastRequested w=" + mLastRequestedWidth + + " h=" + mLastRequestedHeight); } if (mIsChildWindow || mLayoutAttached) { - pw.print(prefix); pw.print("mParentWindow="); pw.print(getParentWindow()); - pw.print(" mLayoutAttached="); pw.println(mLayoutAttached); + pw.println(prefix + "mParentWindow=" + getParentWindow() + + " mLayoutAttached=" + mLayoutAttached); } if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) { - pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow); - pw.print(" mIsWallpaper="); pw.print(mIsWallpaper); - pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer); - pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible); + pw.println(prefix + "mIsImWindow=" + mIsImWindow + + " mIsWallpaper=" + mIsWallpaper + + " mIsFloatingLayer=" + mIsFloatingLayer + + " mWallpaperVisible=" + mWallpaperVisible); } if (dumpAll) { - pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer); - pw.print(" mSubLayer="); pw.print(mSubLayer); - pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+"); - pw.print("="); pw.print(mWinAnimator.mAnimLayer); - pw.print(" mLastLayer="); pw.println(mWinAnimator.mLastLayer); + pw.println(prefix + "mBaseLayer=" + mBaseLayer + + " mSubLayer=" + mSubLayer + + " mAnimLayer=" + mLayer + "=" + mWinAnimator.mAnimLayer + + " mLastLayer=" + mWinAnimator.mLastLayer); } if (dumpAll) { - pw.print(prefix); pw.print("mToken="); pw.println(mToken); + pw.println(prefix + "mToken=" + mToken); if (mAppToken != null) { - pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); - pw.print(prefix); pw.print(" isAnimatingWithSavedSurface()="); - pw.print(" mAppDied=");pw.print(mAppDied); - pw.print(prefix); pw.print("drawnStateEvaluated="); - pw.print(getDrawnStateEvaluated()); - pw.print(prefix); pw.print("mightAffectAllDrawn="); - pw.println(mightAffectAllDrawn()); + pw.println(prefix + "mAppToken=" + mAppToken); + pw.print(prefix + "mAppDied=" + mAppDied); + pw.print(prefix + "drawnStateEvaluated=" + getDrawnStateEvaluated()); + pw.println(prefix + "mightAffectAllDrawn=" + mightAffectAllDrawn()); } - pw.print(prefix); pw.print("mViewVisibility=0x"); - pw.print(Integer.toHexString(mViewVisibility)); - pw.print(" mHaveFrame="); pw.print(mHaveFrame); - pw.print(" mObscured="); pw.println(mObscured); - pw.print(prefix); pw.print("mSeq="); pw.print(mSeq); - pw.print(" mSystemUiVisibility=0x"); - pw.println(Integer.toHexString(mSystemUiVisibility)); + pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility) + + " mHaveFrame=" + mHaveFrame + + " mObscured=" + mObscured); + pw.println(prefix + "mSeq=" + mSeq + + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility)); } if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility - || isParentWindowHidden()|| mPermanentlyHidden || mForceHideNonSystemOverlayWindow + || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow || mHiddenWhileSuspended) { - pw.print(prefix); pw.print("mPolicyVisibility="); - pw.print(mPolicyVisibility); - pw.print(" mPolicyVisibilityAfterAnim="); - pw.print(mPolicyVisibilityAfterAnim); - pw.print(" mAppOpVisibility="); - pw.print(mAppOpVisibility); - pw.print(" parentHidden="); pw.print(isParentWindowHidden()); - pw.print(" mPermanentlyHidden="); pw.print(mPermanentlyHidden); - pw.print(" mHiddenWhileSuspended="); pw.print(mHiddenWhileSuspended); - pw.print(" mForceHideNonSystemOverlayWindow="); pw.println( - mForceHideNonSystemOverlayWindow); + pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility + + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim + + " mAppOpVisibility=" + mAppOpVisibility + + " parentHidden=" + isParentWindowHidden() + + " mPermanentlyHidden=" + mPermanentlyHidden + + " mHiddenWhileSuspended=" + mHiddenWhileSuspended + + " mForceHideNonSystemOverlayWindow=" + mForceHideNonSystemOverlayWindow); } if (!mRelayoutCalled || mLayoutNeeded) { - pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled); - pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded); + pw.println(prefix + "mRelayoutCalled=" + mRelayoutCalled + + " mLayoutNeeded=" + mLayoutNeeded); } if (dumpAll) { - pw.print(prefix); pw.print("mGivenContentInsets="); - mGivenContentInsets.printShortString(pw); - pw.print(" mGivenVisibleInsets="); - mGivenVisibleInsets.printShortString(pw); - pw.println(); + pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString(sTmpSB) + + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString(sTmpSB)); if (mTouchableInsets != 0 || mGivenInsetsPending) { - pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets); - pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending); + pw.println(prefix + "mTouchableInsets=" + mTouchableInsets + + " mGivenInsetsPending=" + mGivenInsetsPending); Region region = new Region(); getTouchableRegion(region); - pw.print(prefix); pw.print("touchable region="); pw.println(region); + pw.println(prefix + "touchable region=" + region); } - pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration()); - pw.print(prefix); pw.print("mLastReportedConfiguration="); - pw.println(getLastReportedConfiguration()); + pw.println(prefix + "mFullConfiguration=" + getConfiguration()); + pw.println(prefix + "mLastReportedConfiguration=" + getLastReportedConfiguration()); } - pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface); - pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay()); - pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed); + pw.println(prefix + "mHasSurface=" + mHasSurface + + " isReadyForDisplay()=" + isReadyForDisplay() + + " mWindowRemovalAllowed=" + mWindowRemovalAllowed); if (mEnforceSizeCompat) { - pw.print(prefix); pw.print("mCompatFrame="); mCompatFrame.printShortString(pw); - pw.println(); + pw.println(prefix + "mCompatFrame=" + mCompatFrame.toShortString(sTmpSB)); } if (dumpAll) { mWindowFrames.dump(pw, prefix); - pw.print(prefix); pw.print("Cur insets: overscan="); - mOverscanInsets.printShortString(pw); - pw.print(" content="); mContentInsets.printShortString(pw); - pw.print(" visible="); mVisibleInsets.printShortString(pw); - pw.print(" stable="); mStableInsets.printShortString(pw); - pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw); - pw.print(" outsets="); mOutsets.printShortString(pw); - pw.print(prefix); pw.print("Lst insets: overscan="); - mLastOverscanInsets.printShortString(pw); - pw.print(" content="); mLastContentInsets.printShortString(pw); - pw.print(" visible="); mLastVisibleInsets.printShortString(pw); - pw.print(" stable="); mLastStableInsets.printShortString(pw); - pw.print(" physical="); mLastOutsets.printShortString(pw); - pw.print(" outset="); mLastOutsets.printShortString(pw); - pw.println(); + pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB) + + " content=" + mContentInsets.toShortString(sTmpSB) + + " visible=" + mVisibleInsets.toShortString(sTmpSB) + + " stable=" + mStableInsets.toShortString(sTmpSB) + + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB) + + " outsets=" + mOutsets.toShortString(sTmpSB)); + pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB) + + " content=" + mLastContentInsets.toShortString(sTmpSB) + + " visible=" + mLastVisibleInsets.toShortString(sTmpSB) + + " stable=" + mLastStableInsets.toShortString(sTmpSB) + + " outset=" + mLastOutsets.toShortString(sTmpSB)); } super.dump(pw, prefix, dumpAll); - pw.print(prefix); pw.print(mWinAnimator); pw.println(":"); + pw.println(prefix + mWinAnimator + ":"); mWinAnimator.dump(pw, prefix + " ", dumpAll); if (mAnimatingExit || mRemoveOnExit || mDestroying || mRemoved) { - pw.print(prefix); pw.print("mAnimatingExit="); pw.print(mAnimatingExit); - pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit); - pw.print(" mDestroying="); pw.print(mDestroying); - pw.print(" mRemoved="); pw.println(mRemoved); + pw.println(prefix + "mAnimatingExit=" + mAnimatingExit + + " mRemoveOnExit=" + mRemoveOnExit + + " mDestroying=" + mDestroying + + " mRemoved=" + mRemoved); } if (getOrientationChanging() || mAppFreezing || mReportOrientationChanged) { - pw.print(prefix); pw.print("mOrientationChanging="); - pw.print(mOrientationChanging); - pw.print(" configOrientationChanging="); - pw.print(getLastReportedConfiguration().orientation - != getConfiguration().orientation); - pw.print(" mAppFreezing="); pw.print(mAppFreezing); - pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged); + pw.println(prefix + "mOrientationChanging=" + mOrientationChanging + + " configOrientationChanging=" + + (getLastReportedConfiguration().orientation != getConfiguration().orientation) + + " mAppFreezing=" + mAppFreezing + + " mReportOrientationChanged=" + mReportOrientationChanged); } if (mLastFreezeDuration != 0) { - pw.print(prefix); pw.print("mLastFreezeDuration="); - TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println(); + pw.print(prefix + "mLastFreezeDuration="); + TimeUtils.formatDuration(mLastFreezeDuration, pw); + pw.println(); } - pw.print(prefix); pw.print("mForceSeamlesslyRotate="); pw.print(mForceSeamlesslyRotate); - pw.print(" seamlesslyRotate: pending="); + pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate + + " seamlesslyRotate: pending="); if (mPendingSeamlessRotate != null) { mPendingSeamlessRotate.dump(pw); } else { pw.print("null"); } - pw.print(" finishedFrameNumber="); pw.print(mFinishSeamlessRotateFrameNumber); - pw.println(); + pw.println(" finishedFrameNumber=" + mFinishSeamlessRotateFrameNumber); if (mHScale != 1 || mVScale != 1) { - pw.print(prefix); pw.print("mHScale="); pw.print(mHScale); - pw.print(" mVScale="); pw.println(mVScale); + pw.println(prefix + "mHScale=" + mHScale + + " mVScale=" + mVScale); } if (mWallpaperX != -1 || mWallpaperY != -1) { - pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX); - pw.print(" mWallpaperY="); pw.println(mWallpaperY); + pw.println(prefix + "mWallpaperX=" + mWallpaperX + + " mWallpaperY=" + mWallpaperY); } if (mWallpaperXStep != -1 || mWallpaperYStep != -1) { - pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep); - pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep); + pw.println(prefix + "mWallpaperXStep=" + mWallpaperXStep + + " mWallpaperYStep=" + mWallpaperYStep); } if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE || mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { - pw.print(prefix); pw.print("mWallpaperDisplayOffsetX="); - pw.print(mWallpaperDisplayOffsetX); - pw.print(" mWallpaperDisplayOffsetY="); - pw.println(mWallpaperDisplayOffsetY); + pw.println(prefix + "mWallpaperDisplayOffsetX=" + mWallpaperDisplayOffsetX + + " mWallpaperDisplayOffsetY=" + mWallpaperDisplayOffsetY); } if (mDrawLock != null) { - pw.print(prefix); pw.println("mDrawLock=" + mDrawLock); + pw.println(prefix + "mDrawLock=" + mDrawLock); } if (isDragResizing()) { - pw.print(prefix); pw.println("isDragResizing=" + isDragResizing()); + pw.println(prefix + "isDragResizing=" + isDragResizing()); } if (computeDragResizing()) { - pw.print(prefix); pw.println("computeDragResizing=" + computeDragResizing()); + pw.println(prefix + "computeDragResizing=" + computeDragResizing()); } - pw.print(prefix); pw.println("isOnScreen=" + isOnScreen()); - pw.print(prefix); pw.println("isVisible=" + isVisible()); + pw.println(prefix + "isOnScreen=" + isOnScreen()); + pw.println(prefix + "isVisible=" + isVisible()); } @Override @@ -4934,9 +4912,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("from="); pw.print(mFrom); - pw.print(" to="); pw.print(mTo); - pw.print(" duration="); pw.println(mDuration); + pw.println(prefix + "from=" + mFrom + + " to=" + mTo + + " duration=" + mDuration); } @Override diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 52f2d674f993..b467d61c355d 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -230,11 +230,11 @@ public: virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices); virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier); - virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier); + virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier); virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env, jfloatArray matrixArr); virtual TouchAffineTransformation getTouchAffineTransformation( - const String8& inputDeviceDescriptor, int32_t surfaceRotation); + const std::string& inputDeviceDescriptor, int32_t surfaceRotation); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -480,7 +480,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon for (jsize i = 0; i < length; i++) { jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i)); const char* deviceNameChars = env->GetStringUTFChars(item, NULL); - outConfig->excludedDeviceNames.add(String8(deviceNameChars)); + outConfig->excludedDeviceNames.push_back(deviceNameChars); env->ReleaseStringUTFChars(item, deviceNameChars); env->DeleteLocalRef(item); } @@ -606,7 +606,7 @@ sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay( JNIEnv* env = jniEnv(); sp<KeyCharacterMap> result; - ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.string())); + ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.c_str())); ScopedLocalRef<jobject> identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz, gInputDeviceIdentifierInfo.constructor, descriptor.get(), identifier.vendor, identifier.product)); @@ -620,24 +620,24 @@ sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay( ScopedUtfChars filenameChars(env, filenameObj.get()); ScopedUtfChars contentsChars(env, contentsObj.get()); - KeyCharacterMap::loadContents(String8(filenameChars.c_str()), - String8(contentsChars.c_str()), KeyCharacterMap::FORMAT_OVERLAY, &result); + KeyCharacterMap::loadContents(filenameChars.c_str(), + contentsChars.c_str(), KeyCharacterMap::FORMAT_OVERLAY, &result); } checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay"); return result; } -String8 NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) { +std::string NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) { ATRACE_CALL(); JNIEnv* env = jniEnv(); - ScopedLocalRef<jstring> uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.string())); + ScopedLocalRef<jstring> uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.c_str())); ScopedLocalRef<jstring> aliasObj(env, jstring(env->CallObjectMethod(mServiceObj, gServiceClassInfo.getDeviceAlias, uniqueIdObj.get()))); - String8 result; + std::string result; if (aliasObj.get()) { ScopedUtfChars aliasChars(env, aliasObj.get()); - result.setTo(aliasChars.c_str()); + result = aliasChars.c_str(); } checkAndClearExceptionFromCallback(env, "getDeviceAlias"); return result; @@ -932,10 +932,10 @@ TouchAffineTransformation NativeInputManager::getTouchAffineTransformation( } TouchAffineTransformation NativeInputManager::getTouchAffineTransformation( - const String8& inputDeviceDescriptor, int32_t surfaceRotation) { + const std::string& inputDeviceDescriptor, int32_t surfaceRotation) { JNIEnv* env = jniEnv(); - ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string())); + ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.c_str())); jobject cal = env->CallObjectMethod(mServiceObj, gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get(), @@ -1281,7 +1281,7 @@ static void nativeSetDisplayViewport(JNIEnv* env, jclass /* clazz */, jlong ptr, v.deviceWidth = deviceWidth; v.deviceHeight = deviceHeight; if (uniqueId != nullptr) { - v.uniqueId.setTo(ScopedUtfChars(env, uniqueId).c_str()); + v.uniqueId = ScopedUtfChars(env, uniqueId).c_str(); } im->setDisplayViewport(viewportType, v); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ecc13b295539..6431344f218b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -747,6 +747,11 @@ public final class SystemServer { traceBeginAndSlog("StartBinderCallsStatsService"); mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class); traceEnd(); + + // Tracks time spent in handling messages in handlers. + traceBeginAndSlog("StartLooperStatsService"); + mSystemServiceManager.startService(LooperStatsService.Lifecycle.class); + traceEnd(); } /** diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk new file mode 100644 index 000000000000..34de9dfb7ad6 --- /dev/null +++ b/services/tests/mockingservicestests/Android.mk @@ -0,0 +1,45 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + services.core \ + services.devicepolicy \ + frameworks-base-testutils \ + androidx-test \ + mockito-target-extended-minus-junit4 \ + ShortcutManagerTestUtils \ + compatibility-device-util \ + truth-prebuilt + +LOCAL_JAVA_LIBRARIES := \ + android.test.mock + +LOCAL_JNI_SHARED_LIBRARIES := \ + libdexmakerjvmtiagent \ + libstaticjvmtiagent + +LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests +LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml new file mode 100644 index 000000000000..247e446d1bfc --- /dev/null +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.mockingservicestests"> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.mockingservicestests" + android:label="Frameworks Mocking Services Tests" /> +</manifest> diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml new file mode 100644 index 000000000000..adfee96d4e28 --- /dev/null +++ b/services/tests/mockingservicestests/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs Frameworks Services Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="FrameworksMockingServicesTests.apk" /> + </target_preparer> + + <option name="test-tag" value="FrameworksMockingServicesTests" /> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.frameworks.mockingservicestests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java index 910aad7fd86e..c8e67820ce46 100644 --- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java @@ -64,9 +64,6 @@ import android.test.mock.MockContentResolver; import android.util.ArraySet; import android.util.Pair; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.server.AppStateTracker.Listener; @@ -88,11 +85,14 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + /** * Tests for {@link AppStateTracker} * * Run with: - atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java + atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -683,10 +683,10 @@ public class AppStateTrackerTest { //-------------------------------------------------- List<OpEntry> entries = new ArrayList<>(); - entries.add(new AppOpsManager.OpEntry( + entries.add(new OpEntry( AppOpsManager.OP_ACCESS_NOTIFICATIONS, AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); - entries.add(new AppOpsManager.OpEntry( + entries.add(new OpEntry( AppStateTracker.TARGET_OP, AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); @@ -694,7 +694,7 @@ public class AppStateTrackerTest { //-------------------------------------------------- entries = new ArrayList<>(); - entries.add(new AppOpsManager.OpEntry( + entries.add(new OpEntry( AppStateTracker.TARGET_OP, AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); @@ -702,7 +702,7 @@ public class AppStateTrackerTest { //-------------------------------------------------- entries = new ArrayList<>(); - entries.add(new AppOpsManager.OpEntry( + entries.add(new OpEntry( AppStateTracker.TARGET_OP, AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null)); @@ -710,10 +710,10 @@ public class AppStateTrackerTest { //-------------------------------------------------- entries = new ArrayList<>(); - entries.add(new AppOpsManager.OpEntry( + entries.add(new OpEntry( AppStateTracker.TARGET_OP, AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); - entries.add(new AppOpsManager.OpEntry( + entries.add(new OpEntry( AppOpsManager.OP_ACCESS_NOTIFICATIONS, AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null)); @@ -754,7 +754,7 @@ public class AppStateTrackerTest { final AppStateTrackerTestable instance = newInstance(); callStart(instance); - AppStateTracker.Listener l = mock(AppStateTracker.Listener.class); + Listener l = mock(Listener.class); instance.addListener(l); // Power save on. @@ -797,7 +797,7 @@ public class AppStateTrackerTest { final AppStateTrackerTestable instance = newInstance(); callStart(instance); - AppStateTracker.Listener l = mock(AppStateTracker.Listener.class); + Listener l = mock(Listener.class); instance.addListener(l); // ------------------------------------------------------------------------- diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index ece9f42bd0eb..79a654b2c0f4 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -52,6 +52,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,6 +84,8 @@ public class BrightnessTrackerTest { private static HandlerThread sThread = new HandlerThread("brightness.test", android.os.Process.THREAD_PRIORITY_BACKGROUND); + private int mDefaultNightModeColorTemperature; + private static Handler ensureHandler() { synchronized (sHandlerLock) { if (sHandler == null) { @@ -98,6 +102,9 @@ public class BrightnessTrackerTest { mInjector = new TestInjector(ensureHandler()); mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector); + mDefaultNightModeColorTemperature = + InstrumentationRegistry.getContext().getResources().getInteger( + R.integer.config_nightDisplayColorTemperatureDefault); } @Test @@ -188,7 +195,7 @@ public class BrightnessTrackerTest { // System had no data so these should all be at defaults. assertEquals(Float.NaN, event.batteryLevel, 0.0); assertFalse(event.nightMode); - assertEquals(0, event.colorTemperature); + assertEquals(mDefaultNightModeColorTemperature, event.colorTemperature); } @Test @@ -863,5 +870,17 @@ public class BrightnessTrackerTest { public boolean isInteractive(Context context) { return mInteractive; } + + @Override + public int getColorTemperature(Context context, int userId) { + return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, + mDefaultNightModeColorTemperature); + } + + @Override + public boolean isNightModeActive(Context context, int userId) { + return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, + 0) == 1; + } } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index 5a42a848ef92..b43d9a671751 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -50,13 +50,19 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.graphics.Matrix; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.util.Size; +import android.view.DisplayCutout; import android.view.SurfaceControl; import android.view.WindowManager; +import com.android.server.wm.utils.WmDisplayCutout; + import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.LinkedList; import androidx.test.filters.FlakyTest; @@ -382,6 +388,20 @@ public class WindowStateTests extends WindowTestsBase { } } + @Test + public void testDisplayCutoutIsCalculatedRelativeToFrame() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + WindowFrames wf = new WindowFrames(); + wf.mParentFrame.set(7, 10, 185, 380); + wf.mDisplayFrame.set(wf.mParentFrame); + final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22), + Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400))); + wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400))); + + app.computeFrameLw(wf); + assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20))); + } + private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) { reset(mPowerManagerWrapper); final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 2f1c5169362a..4874bcef1606 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -44,6 +44,10 @@ public final class UsbAlsaManager { private static final String TAG = UsbAlsaManager.class.getSimpleName(); private static final boolean DEBUG = false; + // Flag to turn on/off multi-peripheral select mode + // Set to true to have single-device-only mode + private static final boolean mIsSingleMode = true; + private static final String ALSA_DIRECTORY = "/dev/snd/"; private final Context mContext; @@ -84,10 +88,11 @@ public final class UsbAlsaManager { */ private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) { if (DEBUG) { - Slog.d(TAG, "selectAlsaDevice " + alsaDevice); + Slog.d(TAG, "selectAlsaDevice() " + alsaDevice); } - if (mSelectedDevice != null) { + // This must be where an existing USB audio device is deselected.... (I think) + if (mIsSingleMode && mSelectedDevice != null) { deselectAlsaDevice(); } @@ -104,9 +109,15 @@ public final class UsbAlsaManager { mSelectedDevice = alsaDevice; alsaDevice.start(); + if (DEBUG) { + Slog.d(TAG, "selectAlsaDevice() - done."); + } } private synchronized void deselectAlsaDevice() { + if (DEBUG) { + Slog.d(TAG, "deselectAlsaDevice() mSelectedDevice " + mSelectedDevice); + } if (mSelectedDevice != null) { mSelectedDevice.stop(); mSelectedDevice = null; @@ -133,7 +144,7 @@ public final class UsbAlsaManager { /* package */ UsbAlsaDevice selectDefaultDevice() { if (DEBUG) { - Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()"); + Slog.d(TAG, "selectDefaultDevice()"); } if (mAlsaDevices.size() > 0) { @@ -230,6 +241,8 @@ public final class UsbAlsaManager { } } + logDevices("deviceAdded()"); + if (DEBUG) { Slog.d(TAG, "deviceAdded() - done"); } @@ -254,6 +267,9 @@ public final class UsbAlsaManager { Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice); IoUtils.closeQuietly(usbMidiDevice); } + + logDevices("usbDeviceRemoved()"); + } /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) { @@ -296,6 +312,7 @@ public final class UsbAlsaManager { /** * Dump the USB alsa state. */ + // invoked with "adb shell dumpsys usb" public void dump(DualDumpOutputStream dump, String idName, long id) { long token = dump.start(idName, id); @@ -314,29 +331,26 @@ public final class UsbAlsaManager { dump.end(token); } -/* public void logDevicesList(String title) { - if (DEBUG) { - for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) { - Slog.i(TAG, "UsbDevice-------------------"); - Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]")); - Slog.i(TAG, "UsbAlsaDevice--------------"); - Slog.i(TAG, "" + entry.getValue()); - } - } + if (DEBUG) { + Slog.i(TAG, title + "----------------"); + for (UsbAlsaDevice alsaDevice : mAlsaDevices) { + Slog.i(TAG, " -->"); + Slog.i(TAG, "" + alsaDevice); + Slog.i(TAG, " <--"); + } + Slog.i(TAG, "----------------"); + } } -*/ // This logs a more terse (and more readable) version of the devices list -/* public void logDevices(String title) { - if (DEBUG) { - Slog.i(TAG, title); - for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) { - Slog.i(TAG, entry.getValue().toShortString()); - } - } + if (DEBUG) { + Slog.i(TAG, title + "----------------"); + for (UsbAlsaDevice alsaDevice : mAlsaDevices) { + Slog.i(TAG, alsaDevice.toShortString()); + } + Slog.i(TAG, "----------------"); + } } -*/ - } diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java new file mode 100644 index 000000000000..1730d8f22950 --- /dev/null +++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.usb; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.UserHandle; +import android.util.Slog; + +import java.util.ArrayList; + +/** + * UsbResolveActivityManager creates UI dialogs for user to pick or confirm handler for + * usb attach event. + * + * @hide + */ +class UsbHandlerManager { + private static final String LOG_TAG = UsbHandlerManager.class.getSimpleName(); + + private final Context mContext; + + UsbHandlerManager(@NonNull Context context) { + mContext = context; + } + + /** + * Shows dialog to user to allow them to optionally visit that URL for more + * information or software downloads if the attached USB accessory has a valid + * URL associated with it. + * + * @param accessory The accessory to confirm in the UI dialog + * @param user The user to start the UI dialog + */ + void showUsbAccessoryUriActivity(@NonNull UsbAccessory accessory, + @NonNull UserHandle user) { + String uri = accessory.getUri(); + if (uri != null && uri.length() > 0) { + // display URI to user + Intent dialogIntent = createDialogIntent(); + dialogIntent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbAccessoryUriActivity"); + dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + dialogIntent.putExtra("uri", uri); + try { + mContext.startActivityAsUser(dialogIntent, user); + } catch (ActivityNotFoundException e) { + Slog.e(LOG_TAG, "unable to start UsbAccessoryUriActivity"); + } + } + } + + /** + * Shows dialog to user to confirm the package to start when the USB device + * or accessory is attached and there is only one package claims to handle this + * USB device or accessory. + * + * @param rInfo The ResolveInfo of the package to confirm in the UI dialog + * @param device The USB device to confirm + * @param accessory The USB accessory to confirm + */ + void confirmUsbHandler(@NonNull ResolveInfo rInfo, @Nullable UsbDevice device, + @Nullable UsbAccessory accessory) { + Intent resolverIntent = createDialogIntent(); + // start UsbConfirmActivity if there is only one choice + resolverIntent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbConfirmActivity"); + resolverIntent.putExtra("rinfo", rInfo); + UserHandle user = + UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid); + + if (device != null) { + resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device); + } else { + resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + } + + try { + mContext.startActivityAsUser(resolverIntent, user); + } catch (ActivityNotFoundException e) { + Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e); + } + } + + /** + * Shows dialog to user to select the package to start when the USB device + * or accessory is attached and there are more than one package claim to handle this + * USB device or accessory. + * + * @param matches The available resolutions of the intent + * @param user The user to start UI dialog + * @param intent The intent to start the UI dialog + */ + void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches, + @NonNull UserHandle user, @NonNull Intent intent) { + Intent resolverIntent = createDialogIntent(); + resolverIntent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbResolverActivity"); + resolverIntent.putParcelableArrayListExtra("rlist", matches); + resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); + + try { + mContext.startActivityAsUser(resolverIntent, user); + } catch (ActivityNotFoundException e) { + Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e); + } + } + + private Intent createDialogIntent() { + Intent intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return intent; + } +} diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 67ad0907181b..589bcdc14871 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -408,11 +408,15 @@ public class UsbHostManager { /* Called from JNI in monitorUsbHostBus to report USB device removal */ @SuppressWarnings("unused") private void usbDeviceRemoved(String deviceAddress) { + if (DEBUG) { + Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end"); + } + synchronized (mLock) { UsbDevice device = mDevices.remove(deviceAddress); if (device != null) { Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName()); - mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/); + mUsbAlsaManager.usbDeviceRemoved(deviceAddress); mSettingsManager.usbDeviceRemoved(device); getCurrentUserSettings().usbDeviceRemoved(device); diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java new file mode 100644 index 000000000000..2c9ee36107e8 --- /dev/null +++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.usb; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.os.Binder; +import android.os.Process; +import android.os.UserHandle; +import android.service.usb.UsbSettingsAccessoryPermissionProto; +import android.service.usb.UsbSettingsDevicePermissionProto; +import android.service.usb.UsbUserSettingsManagerProto; +import android.util.Slog; +import android.util.SparseBooleanArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.dump.DualDumpOutputStream; + +import java.util.HashMap; + +/** + * UsbPermissionManager manages usb device or accessory access permissions. + * + * @hide + */ +class UsbPermissionManager { + private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName(); + + @GuardedBy("mLock") + /** Temporary mapping USB device name to list of UIDs with permissions for the device*/ + private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = + new HashMap<>(); + @GuardedBy("mLock") + /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/ + private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = + new HashMap<>(); + + private final UserHandle mUser; + private final boolean mDisablePermissionDialogs; + + private final Object mLock = new Object(); + + UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) { + mUser = user; + mDisablePermissionDialogs = context.getResources().getBoolean( + com.android.internal.R.bool.config_disableUsbPermissionDialogs); + } + + /** + * Removes access permissions of all packages for the USB accessory. + * + * @param accessory to remove permissions for + */ + void removeAccessoryPermissions(@NonNull UsbAccessory accessory) { + synchronized (mLock) { + mAccessoryPermissionMap.remove(accessory); + } + } + + /** + * Removes access permissions of all packages for the USB device. + * + * @param device to remove permissions for + */ + void removeDevicePermissions(@NonNull UsbDevice device) { + synchronized (mLock) { + mDevicePermissionMap.remove(device.getDeviceName()); + } + } + + /** + * Grants permission for USB device without showing system dialog for package with uid. + * + * @param device to grant permission for + * @param uid to grant permission for + */ + void grantDevicePermission(@NonNull UsbDevice device, int uid) { + synchronized (mLock) { + String deviceName = device.getDeviceName(); + SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); + if (uidList == null) { + uidList = new SparseBooleanArray(1); + mDevicePermissionMap.put(deviceName, uidList); + } + uidList.put(uid, true); + } + } + + /** + * Grants permission for USB accessory without showing system dialog for package with uid. + * + * @param accessory to grant permission for + * @param uid to grant permission for + */ + void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) { + synchronized (mLock) { + SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); + if (uidList == null) { + uidList = new SparseBooleanArray(1); + mAccessoryPermissionMap.put(accessory, uidList); + } + uidList.put(uid, true); + } + } + + /** + * Returns true if package with uid has permission to access the device. + * + * @param device to check permission for + * @param uid to check permission for + * @return {@code true} if package with uid has permission + */ + boolean hasPermission(@NonNull UsbDevice device, int uid) { + synchronized (mLock) { + if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { + return true; + } + SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); + if (uidList == null) { + return false; + } + return uidList.get(uid); + } + } + + /** + * Returns true if caller has permission to access the accessory. + * + * @param accessory to check permission for + * @return {@code true} if caller has permssion + */ + boolean hasPermission(@NonNull UsbAccessory accessory) { + synchronized (mLock) { + int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { + return true; + } + SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); + if (uidList == null) { + return false; + } + return uidList.get(uid); + } + } + + /** + * Creates UI dialog to request permission for the given package to access the device + * or accessory. + * + * @param device The USB device attached + * @param accessory The USB accessory attached + * @param canBeDefault Whether the calling pacakge can set as default handler + * of the USB device or accessory + * @param packageName The package name of the calling package + * @param uid The uid of the calling package + * @param userContext The context to start the UI dialog + * @param pi PendingIntent for returning result + */ + void requestPermissionDialog(@Nullable UsbDevice device, + @Nullable UsbAccessory accessory, + boolean canBeDefault, + @NonNull String packageName, + int uid, + @NonNull Context userContext, + @NonNull PendingIntent pi) { + long identity = Binder.clearCallingIdentity(); + Intent intent = new Intent(); + if (device != null) { + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + } else { + intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); + } + intent.putExtra(Intent.EXTRA_INTENT, pi); + intent.putExtra(Intent.EXTRA_UID, uid); + intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault); + intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName); + intent.setClassName("com.android.systemui", + "com.android.systemui.usb.UsbPermissionActivity"); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + try { + userContext.startActivityAsUser(intent, mUser); + } catch (ActivityNotFoundException e) { + Slog.e(LOG_TAG, "unable to start UsbPermissionActivity"); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + void dump(@NonNull DualDumpOutputStream dump) { + synchronized (mLock) { + for (String deviceName : mDevicePermissionMap.keySet()) { + long devicePermissionToken = dump.start("device_permissions", + UsbUserSettingsManagerProto.DEVICE_PERMISSIONS); + + dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName); + + SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); + int count = uidList.size(); + for (int i = 0; i < count; i++) { + dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i)); + } + + dump.end(devicePermissionToken); + } + + for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { + long accessoryPermissionToken = dump.start("accessory_permissions", + UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS); + + dump.write("accessory_description", + UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION, + accessory.getDescription()); + + SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); + int count = uidList.size(); + for (int i = 0; i < count; i++) { + dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i)); + } + + dump.end(accessoryPermissionToken); + } + } + } +} diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index 7a906d0b7aaf..1ab1f7e43906 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -193,6 +193,8 @@ class UsbProfileGroupSettingsManager { MyPackageMonitor mPackageMonitor = new MyPackageMonitor(); + private final UsbHandlerManager mUsbHandlerManager; + private final MtpNotificationManager mMtpNotificationManager; /** @@ -201,9 +203,11 @@ class UsbProfileGroupSettingsManager { * @param context The context of the service * @param user The parent profile * @param settingsManager The settings manager of the service + * @param usbResolveActivityManager The resovle activity manager of the service */ UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user, - @NonNull UsbSettingsManager settingsManager) { + @NonNull UsbSettingsManager settingsManager, + @NonNull UsbHandlerManager usbResolveActivityManager) { if (DEBUG) Slog.v(TAG, "Creating settings for " + user); Context parentUserContext; @@ -238,6 +242,8 @@ class UsbProfileGroupSettingsManager { parentUserContext, device -> resolveActivity(createDeviceAttachedIntent(device), device, false /* showMtpNotification */)); + + mUsbHandlerManager = usbResolveActivityManager; } /** @@ -830,23 +836,8 @@ class UsbProfileGroupSettingsManager { // don't show the resolver activity if there are no choices available if (matches.size() == 0) { if (accessory != null) { - String uri = accessory.getUri(); - if (uri != null && uri.length() > 0) { - // display URI to user - Intent dialogIntent = new Intent(); - dialogIntent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbAccessoryUriActivity"); - dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); - dialogIntent.putExtra("uri", uri); - try { - mContext.startActivityAsUser(dialogIntent, mParentUser); - } catch (ActivityNotFoundException e) { - Slog.e(TAG, "unable to start UsbAccessoryUriActivity"); - } - } + mUsbHandlerManager.showUsbAccessoryUriActivity(accessory, mParentUser); } - // do nothing return; } @@ -875,37 +866,10 @@ class UsbProfileGroupSettingsManager { Slog.e(TAG, "startActivity failed", e); } } else { - Intent resolverIntent = new Intent(); - resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - UserHandle user; - if (matches.size() == 1) { - ResolveInfo rInfo = matches.get(0); - - // start UsbConfirmActivity if there is only one choice - resolverIntent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbConfirmActivity"); - resolverIntent.putExtra("rinfo", rInfo); - user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid); - - if (device != null) { - resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device); - } else { - resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); - } + mUsbHandlerManager.confirmUsbHandler(matches.get(0), device, accessory); } else { - user = mParentUser; - - // start UsbResolverActivity so user can choose an activity - resolverIntent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbResolverActivity"); - resolverIntent.putParcelableArrayListExtra("rlist", matches); - resolverIntent.putExtra(Intent.EXTRA_INTENT, intent); - } - try { - mContext.startActivityAsUser(resolverIntent, user); - } catch (ActivityNotFoundException e) { - Slog.e(TAG, "unable to start activity " + resolverIntent, e); + mUsbHandlerManager.selectUsbHandler(matches, mParentUser, intent); } } } diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java index 9221825b2d87..27566f04c280 100644 --- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java @@ -57,10 +57,12 @@ class UsbSettingsManager { private final SparseArray<UsbProfileGroupSettingsManager> mSettingsByProfileGroup = new SparseArray<>(); private UserManager mUserManager; + private UsbHandlerManager mUsbHandlerManager; public UsbSettingsManager(@NonNull Context context) { mContext = context; mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mUsbHandlerManager = new UsbHandlerManager(context); } /** @@ -74,7 +76,8 @@ class UsbSettingsManager { synchronized (mSettingsByUser) { UsbUserSettingsManager settings = mSettingsByUser.get(userId); if (settings == null) { - settings = new UsbUserSettingsManager(mContext, new UserHandle(userId)); + settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId), + new UsbPermissionManager(mContext, UserHandle.of(userId))); mSettingsByUser.put(userId, settings); } return settings; @@ -102,7 +105,8 @@ class UsbSettingsManager { UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.get( parentUser.getIdentifier()); if (settings == null) { - settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this); + settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this, + mUsbHandlerManager); mSettingsByProfileGroup.put(parentUser.getIdentifier(), settings); } return settings; diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java index 24a2d72415ed..fe93399c2d60 100644 --- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java @@ -21,15 +21,18 @@ import static com.android.server.usb.UsbProfileGroupSettingsManager.getAccessory import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PendingIntent; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.res.XmlResourceParser; import android.hardware.usb.AccessoryFilter; import android.hardware.usb.DeviceFilter; import android.hardware.usb.UsbAccessory; @@ -38,42 +41,34 @@ import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.Binder; -import android.os.Process; import android.os.UserHandle; import android.service.usb.UsbAccessoryAttachedActivities; import android.service.usb.UsbDeviceAttachedActivities; -import android.service.usb.UsbSettingsAccessoryPermissionProto; -import android.service.usb.UsbSettingsDevicePermissionProto; import android.service.usb.UsbUserSettingsManagerProto; import android.util.Slog; -import android.util.SparseBooleanArray; +import com.android.internal.util.XmlUtils; import com.android.internal.util.dump.DualDumpOutputStream; +import org.xmlpull.v1.XmlPullParser; + import java.util.ArrayList; -import java.util.HashMap; import java.util.List; class UsbUserSettingsManager { - private static final String TAG = "UsbUserSettingsManager"; + private static final String TAG = UsbUserSettingsManager.class.getSimpleName(); private static final boolean DEBUG = false; private final UserHandle mUser; - private final boolean mDisablePermissionDialogs; private final Context mUserContext; private final PackageManager mPackageManager; - - // Temporary mapping USB device name to list of UIDs with permissions for the device - private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = - new HashMap<>(); - // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory - private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = - new HashMap<>(); + private final UsbPermissionManager mUsbPermissionManager; private final Object mLock = new Object(); - public UsbUserSettingsManager(Context context, UserHandle user) { + UsbUserSettingsManager(Context context, UserHandle user, + @NonNull UsbPermissionManager usbPermissionManager) { if (DEBUG) Slog.v(TAG, "Creating settings for " + user); try { @@ -85,9 +80,7 @@ class UsbUserSettingsManager { mPackageManager = mUserContext.getPackageManager(); mUser = user; - - mDisablePermissionDialogs = context.getResources().getBoolean( - com.android.internal.R.bool.config_disableUsbPermissionDialogs); + mUsbPermissionManager = usbPermissionManager; } /** @@ -96,9 +89,7 @@ class UsbUserSettingsManager { * @param device The device the permissions are for */ void removeDevicePermissions(@NonNull UsbDevice device) { - synchronized (mLock) { - mDevicePermissionMap.remove(device.getDeviceName()); - } + mUsbPermissionManager.removeDevicePermissions(device); } /** @@ -107,9 +98,7 @@ class UsbUserSettingsManager { * @param accessory The accessory the permissions are for */ void removeAccessoryPermissions(@NonNull UsbAccessory accessory) { - synchronized (mLock) { - mAccessoryPermissionMap.remove(accessory); - } + mUsbPermissionManager.removeAccessoryPermissions(accessory); } /** @@ -170,35 +159,17 @@ class UsbUserSettingsManager { } public boolean hasPermission(UsbDevice device, String packageName, int uid) { - synchronized (mLock) { - if (isCameraDevicePresent(device)) { - if (!isCameraPermissionGranted(packageName, uid)) { - return false; - } - } - if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { - return true; - } - SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); - if (uidList == null) { + if (isCameraDevicePresent(device)) { + if (!isCameraPermissionGranted(packageName, uid)) { return false; } - return uidList.get(uid); } + + return mUsbPermissionManager.hasPermission(device, uid); } public boolean hasPermission(UsbAccessory accessory) { - synchronized (mLock) { - int uid = Binder.getCallingUid(); - if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { - return true; - } - SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); - if (uidList == null) { - return false; - } - return uidList.get(uid); - } + return mUsbPermissionManager.hasPermission(accessory); } public void checkPermission(UsbDevice device, String packageName, int uid) { @@ -213,7 +184,11 @@ class UsbUserSettingsManager { } } - private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { + private void requestPermissionDialog(@Nullable UsbDevice device, + @Nullable UsbAccessory accessory, + boolean canBeDefault, + String packageName, + PendingIntent pi) { final int uid = Binder.getCallingUid(); // compare uid with packageName to foil apps pretending to be someone else @@ -227,27 +202,15 @@ class UsbUserSettingsManager { throw new IllegalArgumentException("package " + packageName + " not found"); } - long identity = Binder.clearCallingIdentity(); - intent.setClassName("com.android.systemui", - "com.android.systemui.usb.UsbPermissionActivity"); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(Intent.EXTRA_INTENT, pi); - intent.putExtra("package", packageName); - intent.putExtra(Intent.EXTRA_UID, uid); - try { - mUserContext.startActivityAsUser(intent, mUser); - } catch (ActivityNotFoundException e) { - Slog.e(TAG, "unable to start UsbPermissionActivity"); - } finally { - Binder.restoreCallingIdentity(identity); - } + mUsbPermissionManager.requestPermissionDialog(device, + accessory, canBeDefault, packageName, uid, mUserContext, pi); } public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) { - Intent intent = new Intent(); + Intent intent = new Intent(); // respond immediately if permission has already been granted - if (hasPermission(device, packageName, uid)) { + if (hasPermission(device, packageName, uid)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); try { @@ -270,16 +233,13 @@ class UsbUserSettingsManager { } } - // start UsbPermissionActivity so user can choose an activity - intent.putExtra(UsbManager.EXTRA_DEVICE, device); - requestPermissionDialog(intent, packageName, pi); + requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi); } public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) { - Intent intent = new Intent(); - // respond immediately if permission has already been granted if (hasPermission(accessory)) { + Intent intent = new Intent(); intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); try { @@ -290,31 +250,16 @@ class UsbUserSettingsManager { return; } - intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); - requestPermissionDialog(intent, packageName, pi); + requestPermissionDialog(null, accessory, + canBeDefault(accessory, packageName), packageName, pi); } public void grantDevicePermission(UsbDevice device, int uid) { - synchronized (mLock) { - String deviceName = device.getDeviceName(); - SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); - if (uidList == null) { - uidList = new SparseBooleanArray(1); - mDevicePermissionMap.put(deviceName, uidList); - } - uidList.put(uid, true); - } + mUsbPermissionManager.grantDevicePermission(device, uid); } public void grantAccessoryPermission(UsbAccessory accessory, int uid) { - synchronized (mLock) { - SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); - if (uidList == null) { - uidList = new SparseBooleanArray(1); - mAccessoryPermissionMap.put(accessory, uidList); - } - uidList.put(uid, true); - } + mUsbPermissionManager.grantAccessoryPermission(accessory, uid); } /** @@ -329,42 +274,108 @@ class UsbUserSettingsManager { mUser.getIdentifier()); } - public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) { - long token = dump.start(idName, id); + /** + * Can the app be the default for the USB device. I.e. can the app be launched by default if + * the device is plugged in. + * + * @param device The device the app would be default for + * @param packageName The package name of the app + * + * @return {@code true} if the app can be default + */ + private boolean canBeDefault(@NonNull UsbDevice device, String packageName) { + ActivityInfo[] activities = getPackageActivities(packageName); + if (activities != null) { + int numActivities = activities.length; + for (int i = 0; i < numActivities; i++) { + ActivityInfo activityInfo = activities[i]; + + try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager, + UsbManager.ACTION_USB_DEVICE_ATTACHED)) { + if (parser == null) { + continue; + } - synchronized (mLock) { - dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier()); + XmlUtils.nextElement(parser); + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + if ("usb-device".equals(parser.getName())) { + DeviceFilter filter = DeviceFilter.read(parser); + if (filter.matches(device)) { + return true; + } + } - for (String deviceName : mDevicePermissionMap.keySet()) { - long devicePermissionToken = dump.start("device_permissions", - UsbUserSettingsManagerProto.DEVICE_PERMISSIONS); + XmlUtils.nextElement(parser); + } + } catch (Exception e) { + Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e); + } + } + } - dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName); + return false; + } - SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); - int count = uidList.size(); - for (int i = 0; i < count; i++) { - dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i)); - } + /** + * Can the app be the default for the USB accessory. I.e. can the app be launched by default if + * the accessory is plugged in. + * + * @param accessory The accessory the app would be default for + * @param packageName The package name of the app + * + * @return {@code true} if the app can be default + */ + private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) { + ActivityInfo[] activities = getPackageActivities(packageName); + if (activities != null) { + int numActivities = activities.length; + for (int i = 0; i < numActivities; i++) { + ActivityInfo activityInfo = activities[i]; + + try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager, + UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) { + if (parser == null) { + continue; + } - dump.end(devicePermissionToken); - } - for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { - long accessoryPermissionToken = dump.start("accessory_permissions", - UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS); - - dump.write("accessory_description", - UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION, - accessory.getDescription()); - - SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); - int count = uidList.size(); - for (int i = 0; i < count; i++) { - dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i)); - } + XmlUtils.nextElement(parser); + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + if ("usb-accessory".equals(parser.getName())) { + AccessoryFilter filter = AccessoryFilter.read(parser); + if (filter.matches(accessory)) { + return true; + } + } - dump.end(accessoryPermissionToken); + XmlUtils.nextElement(parser); + } + } catch (Exception e) { + Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e); + } } + } + + return false; + } + + private ActivityInfo[] getPackageActivities(String packageName) { + try { + PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA); + return packageInfo.activities; + } catch (PackageManager.NameNotFoundException e) { + // ignore + } + return null; + } + + public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) { + long token = dump.start(idName, id); + + synchronized (mLock) { + dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier()); + + mUsbPermissionManager.dump(dump); List<ResolveInfo> deviceAttachedActivities = queryIntentActivities( new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED)); diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp new file mode 100644 index 000000000000..98f3c955a2d2 --- /dev/null +++ b/tools/processors/unsupportedappusage/Android.bp @@ -0,0 +1,15 @@ + +java_library_host { + name: "unsupportedappusage-annotation-processor", + java_resources: [ + "META-INF/**/*", + ], + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "guava", + "unsupportedappusage-annotation" + ], + use_tools_jar: true, +} diff --git a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000000..4a969d319070 --- /dev/null +++ b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +android.processor.unsupportedappusage.UnsupportedAppUsageProcessor diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java new file mode 100644 index 000000000000..ef2914610f80 --- /dev/null +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.processor.unsupportedappusage; + +import static javax.lang.model.element.ElementKind.PACKAGE; +import static javax.tools.Diagnostic.Kind.ERROR; +import static javax.tools.Diagnostic.Kind.WARNING; + +import android.annotation.UnsupportedAppUsage; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import com.sun.tools.javac.code.Type; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.annotation.processing.Messager; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; + +/** + * Builds a dex signature for a given method or field. + */ +public class SignatureBuilder { + + private static final Map<TypeKind, String> TYPE_MAP = ImmutableMap.<TypeKind, String>builder() + .put(TypeKind.BOOLEAN, "Z") + .put(TypeKind.BYTE, "B") + .put(TypeKind.CHAR, "C") + .put(TypeKind.DOUBLE, "D") + .put(TypeKind.FLOAT, "F") + .put(TypeKind.INT, "I") + .put(TypeKind.LONG, "J") + .put(TypeKind.SHORT, "S") + .put(TypeKind.VOID, "V") + .build(); + + private final Messager mMessager; + + /** + * Exception used internally when we can't build a signature. Whenever this is thrown, an error + * will also be written to the Messager. + */ + private class SignatureBuilderException extends Exception { + public SignatureBuilderException(String message) { + super(message); + } + public void report(Element offendingElement) { + mMessager.printMessage(ERROR, getMessage(), offendingElement); + } + } + + public SignatureBuilder(Messager messager) { + mMessager = messager; + } + + /** + * Returns a list of enclosing elements for the given element, with the package first, and + * excluding the element itself. + */ + private List<Element> getEnclosingElements(Element e) { + List<Element> enclosing = new ArrayList<>(); + e = e.getEnclosingElement(); // don't include the element itself. + while (e != null) { + enclosing.add(e); + e = e.getEnclosingElement(); + } + Collections.reverse(enclosing); + return enclosing; + } + + /** + * Get the dex signature for a clazz, in format "Lpackage/name/Outer$Inner;" + */ + private String getClassSignature(TypeElement clazz) { + StringBuilder sb = new StringBuilder("L"); + for (Element enclosing : getEnclosingElements(clazz)) { + if (enclosing.getKind() == PACKAGE) { + sb.append(((PackageElement) enclosing) + .getQualifiedName() + .toString() + .replace('.', '/')); + sb.append('/'); + } else { + sb.append(enclosing.getSimpleName()).append('$'); + } + + } + return sb + .append(clazz.getSimpleName()) + .append(";") + .toString(); + } + + /** + * Returns the type signature for a given type. For primitive types, a single character. + * For classes, the class signature. For arrays, a "[" preceeding the component type. + */ + private String getTypeSignature(TypeMirror type) throws SignatureBuilderException { + String sig = TYPE_MAP.get(type.getKind()); + if (sig != null) { + return sig; + } + switch (type.getKind()) { + case ARRAY: + return "[" + getTypeSignature(((ArrayType) type).getComponentType()); + case DECLARED: + Element declaring = ((DeclaredType) type).asElement(); + if (!(declaring instanceof TypeElement)) { + throw new SignatureBuilderException( + "Can't handle declared type of kind " + declaring.getKind()); + } + return getClassSignature((TypeElement) declaring); + case TYPEVAR: + Type.TypeVar typeVar = (Type.TypeVar) type; + if (typeVar.getLowerBound().getKind() != TypeKind.NULL) { + return getTypeSignature(typeVar.getLowerBound()); + } else if (typeVar.getUpperBound().getKind() != TypeKind.NULL) { + return getTypeSignature(typeVar.getUpperBound()); + } else { + throw new SignatureBuilderException("Can't handle typevar with no bound"); + } + + default: + throw new SignatureBuilderException("Can't handle type of kind " + type.getKind()); + } + } + + /** + * Get the signature for an executable, either a method or a constructor. + * + * @param name "<init>" for constructor, else the method name + * @param method The executable element in question. + */ + private String getExecutableSignature(CharSequence name, ExecutableElement method) + throws SignatureBuilderException { + StringBuilder sig = new StringBuilder(); + sig.append(getClassSignature((TypeElement) method.getEnclosingElement())) + .append("->") + .append(name) + .append("("); + for (VariableElement param : method.getParameters()) { + sig.append(getTypeSignature(param.asType())); + } + sig.append(")") + .append(getTypeSignature(method.getReturnType())); + return sig.toString(); + } + + private String buildMethodSignature(ExecutableElement method) throws SignatureBuilderException { + return getExecutableSignature(method.getSimpleName(), method); + } + + private String buildConstructorSignature(ExecutableElement cons) + throws SignatureBuilderException { + return getExecutableSignature("<init>", cons); + } + + private String buildFieldSignature(VariableElement field) throws SignatureBuilderException { + StringBuilder sig = new StringBuilder(); + sig.append(getClassSignature((TypeElement) field.getEnclosingElement())) + .append("->") + .append(field.getSimpleName()) + .append(":") + .append(getTypeSignature(field.asType())) + ; + return sig.toString(); + } + + public String buildSignature(Element element) { + UnsupportedAppUsage uba = element.getAnnotation(UnsupportedAppUsage.class); + try { + String signature; + switch (element.getKind()) { + case METHOD: + signature = buildMethodSignature((ExecutableElement) element); + break; + case CONSTRUCTOR: + signature = buildConstructorSignature((ExecutableElement) element); + break; + case FIELD: + signature = buildFieldSignature((VariableElement) element); + break; + default: + return null; + } + // if we have an expected signature on the annotation, warn if it doesn't match. + if (!Strings.isNullOrEmpty(uba.expectedSignature())) { + if (!signature.equals(uba.expectedSignature())) { + mMessager.printMessage( + WARNING, + String.format("Expected signature doesn't match generated signature.\n" + + " Expected: %s\n Generated: %s", + uba.expectedSignature(), signature), + element); + } + } + return signature; + } catch (SignatureBuilderException problem) { + problem.report(element); + return null; + } + } +} diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java new file mode 100644 index 000000000000..1d4c435939db --- /dev/null +++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.processor.unsupportedappusage; + +import static javax.tools.StandardLocation.CLASS_OUTPUT; + +import android.annotation.UnsupportedAppUsage; + +import com.google.common.base.Joiner; +import com.sun.tools.javac.model.JavacElements; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.util.Pair; +import com.sun.tools.javac.util.Position; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Stream; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +/** + * Annotation processor for {@link UnsupportedAppUsage} annotations. + * + * This processor currently outputs two things: + * 1. A greylist.txt containing dex signatures of all annotated elements. + * 2. A CSV file with a mapping of dex signatures to corresponding source positions. + * + * The first will be used at a later stage of the build to add access flags to the dex file. The + * second is used for automating updates to the annotations themselves. + */ +@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage"}) +public class UnsupportedAppUsageProcessor extends AbstractProcessor { + + // Package name for writing output. Output will be written to the "class output" location within + // this package. + private static final String PACKAGE = "unsupportedappusage"; + private static final String INDEX_CSV = "unsupportedappusage_index.csv"; + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + /** + * Write the contents of a stream to a text file, with one line per item. + */ + private void writeToFile(String name, + String headerLine, + Stream<?> contents) throws IOException { + PrintStream out = new PrintStream(processingEnv.getFiler().createResource( + CLASS_OUTPUT, + PACKAGE, + name) + .openOutputStream()); + out.println(headerLine); + contents.forEach(o -> out.println(o)); + if (out.checkError()) { + throw new IOException("Error when writing to " + name); + } + out.close(); + } + + /** + * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element. + */ + private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) { + for (AnnotationMirror m : e.getAnnotationMirrors()) { + TypeElement type = (TypeElement) m.getAnnotationType().asElement(); + if (type.getQualifiedName().toString().equals( + UnsupportedAppUsage.class.getCanonicalName())) { + return m; + } + } + return null; + } + + /** + * Returns a CSV header line for the columns returned by + * {@link #getAnnotationIndex(String, Element)}. + */ + private String getCsvHeaders() { + return Joiner.on(',').join( + "signature", + "file", + "startline", + "startcol", + "endline", + "endcol" + ); + } + + /** + * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation + * attached to it. It returns CSV in the format: + * dex-signature,filename,start-line,start-col,end-line,end-col + * + * The positions refer to the annotation itself, *not* the annotated member. This can therefore + * be used to read just the annotation from the file, and to perform in-place edits on it. + * + * @param signature the dex signature for the element. + * @param annotatedElement The annotated element + * @return A single line of CSV text + */ + private String getAnnotationIndex(String signature, Element annotatedElement) { + JavacElements javacElem = (JavacElements) processingEnv.getElementUtils(); + AnnotationMirror unsupportedAppUsage = + getUnsupportedAppUsageAnnotationMirror(annotatedElement); + Pair<JCTree, JCTree.JCCompilationUnit> pair = + javacElem.getTreeAndTopLevel(annotatedElement, unsupportedAppUsage, null); + Position.LineMap lines = pair.snd.lineMap; + return Joiner.on(",").join( + signature, + pair.snd.getSourceFile().getName(), + lines.getLineNumber(pair.fst.pos().getStartPosition()), + lines.getColumnNumber(pair.fst.pos().getStartPosition()), + lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)), + lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions))); + } + + /** + * This is the main entry point in the processor, called by the compiler. + */ + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith( + UnsupportedAppUsage.class); + if (annotated.size() == 0) { + return true; + } + // build signatures for each annotated member, and put them in a map of signature to member + Map<String, Element> signatureMap = new TreeMap<>(); + SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager()); + for (Element e : annotated) { + String sig = sb.buildSignature(e); + if (sig != null) { + signatureMap.put(sig, e); + } + } + try { + writeToFile(INDEX_CSV, + getCsvHeaders(), + signatureMap.entrySet() + .stream() + .map(e -> getAnnotationIndex(e.getKey() ,e.getValue()))); + } catch (IOException e) { + throw new RuntimeException("Failed to write output", e); + } + return true; + } +} diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp index 6fb278c83f0a..819e75ba1fed 100644 --- a/tools/validatekeymaps/Android.bp +++ b/tools/validatekeymaps/Android.bp @@ -15,6 +15,7 @@ cc_binary_host { ], static_libs: [ + "libbase", "libinput", "libutils", "libcutils", diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index bbfcba6272b2..f31f771004bd 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -18,7 +18,6 @@ #include <input/KeyLayoutMap.h> #include <input/VirtualKeyMap.h> #include <utils/PropertyMap.h> -#include <utils/String8.h> #include <stdarg.h> #include <stdio.h> @@ -98,7 +97,7 @@ static bool validateFile(const char* filename) { case FILETYPE_KEYLAYOUT: { sp<KeyLayoutMap> map; - status_t status = KeyLayoutMap::load(String8(filename), &map); + status_t status = KeyLayoutMap::load(filename, &map); if (status) { error("Error %d parsing key layout file.\n\n", status); return false; @@ -108,7 +107,7 @@ static bool validateFile(const char* filename) { case FILETYPE_KEYCHARACTERMAP: { sp<KeyCharacterMap> map; - status_t status = KeyCharacterMap::load(String8(filename), + status_t status = KeyCharacterMap::load(filename, KeyCharacterMap::FORMAT_ANY, &map); if (status) { error("Error %d parsing key character map file.\n\n", status); @@ -130,7 +129,7 @@ static bool validateFile(const char* filename) { case FILETYPE_VIRTUALKEYDEFINITION: { VirtualKeyMap* map; - status_t status = VirtualKeyMap::load(String8(filename), &map); + status_t status = VirtualKeyMap::load(filename, &map); if (status) { error("Error %d parsing virtual key definition file.\n\n", status); return false; diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java index 7c99c497c54b..14263830660f 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -48,7 +48,7 @@ public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements private MessageDigest mDigester; public WifiAwareAgentNetworkSpecifier() { - // do nothing, already initialized to empty + initialize(); } public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier ns) { diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java index ef9c59f1d7fa..bb02deccb69a 100644 --- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java +++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java @@ -85,15 +85,21 @@ public abstract class ProvisioningCallback { public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11; /** - * The reason code for provisioning failure when a redirect server fails to start. + * The reason code for provisioning failure when a redirect listener fails to start. */ - public static final int OSU_FAILURE_START_REDIRECT_SERVER = 12; + public static final int OSU_FAILURE_START_REDIRECT_LISTENER = 12; + + /** + * The reason code for provisioning failure when a redirect listener timed out to receive a HTTP + * redirect response. + */ + public static final int OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13; /** * The reason code for provisioning failure when there is no OSU activity to listen to * {@link WifiManager#ACTION_PASSPOINT_LAUNCH_OSU_VIEW} intent. */ - public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 13; + public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; /** * The status code for provisioning flow to indicate connecting to OSU AP diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java index 0515e0637e03..657e5a70b53d 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java @@ -18,6 +18,7 @@ package android.net.wifi.aware; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import android.os.Parcel; import android.support.test.filters.SmallTest; @@ -60,6 +61,13 @@ public class WifiAwareAgentNetworkSpecifierTest { WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR); assertEquals(dut, rereadDut); + + // Ensure that individual network specifiers are satisfied by both the original & marshaled + // |WifiAwareNetworkAgentSpecifier instances. + for (WifiAwareNetworkSpecifier ns : nsSet) { + assertTrue(dut.satisfiesAwareNetworkSpecifier(ns)); + assertTrue(rereadDut.satisfiesAwareNetworkSpecifier(ns)); + } } /** |