Merge "Remove dead code"
diff --git a/Android.mk b/Android.mk
index e405345..65d4d24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
update-api: doc-comment-check-docs
# ==== hiddenapi lists =======================================
+ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
@@ -108,6 +109,7 @@
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
+endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS
# Include subdirectory makefiles
# ============================================================
diff --git a/api/current.txt b/api/current.txt
index 7fcee9f..6f64349 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1511,6 +1511,7 @@
field public static final int use32bitAbi = 16844053; // 0x1010515
field public static final int useAppZygote = 16844184; // 0x1010598
field public static final int useDefaultMargins = 16843641; // 0x1010379
+ field public static final int useEmbeddedDex = 16844196; // 0x10105a4
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
field public static final int userVisible = 16843409; // 0x1010291
@@ -6177,6 +6178,7 @@
method public void onTrimMemory(int);
method public boolean onUnbind(android.content.Intent);
method public final void startForeground(int, android.app.Notification);
+ method public final void startForeground(int, @NonNull android.app.Notification, int);
method public final void stopForeground(boolean);
method public final void stopForeground(int);
method public final void stopSelf();
@@ -11929,13 +11931,13 @@
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
- field public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5; // 0x5
- field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4; // 0x4
- field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2; // 0x2
- field public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6; // 0x6
- field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3; // 0x3
- field public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1; // 0x1
- field public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0; // 0x0
+ field public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10
+ field public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1
+ field public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8
+ field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff
+ field public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2
+ field public static final int FOREGROUND_SERVICE_TYPE_NONE = 0; // 0x0
+ field public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 4; // 0x4
field public int flags;
field public String permission;
}
@@ -24743,9 +24745,9 @@
field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
field public static final int KEY_TYPE_RELEASE = 3; // 0x3
field public static final int KEY_TYPE_STREAMING = 1; // 0x1
- field public static final int OFFLINE_LICENSE_INACTIVE = 2; // 0x2
+ field public static final int OFFLINE_LICENSE_STATE_RELEASED = 2; // 0x2
field public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0; // 0x0
- field public static final int OFFLINE_LICENSE_USABLE = 1; // 0x1
+ field public static final int OFFLINE_LICENSE_STATE_USABLE = 1; // 0x1
field public static final String PROPERTY_ALGORITHMS = "algorithms";
field public static final String PROPERTY_DESCRIPTION = "description";
field public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
@@ -49809,6 +49811,7 @@
field public static final int EDGE_RIGHT = 8; // 0x8
field public static final int EDGE_TOP = 1; // 0x1
field public static final int FLAG_WINDOW_IS_OBSCURED = 1; // 0x1
+ field public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 2; // 0x2
field public static final int INVALID_POINTER_ID = -1; // 0xffffffff
field public static final int TOOL_TYPE_ERASER = 4; // 0x4
field public static final int TOOL_TYPE_FINGER = 1; // 0x1
@@ -50361,6 +50364,7 @@
method public final int getScrollX();
method public final int getScrollY();
method @android.view.ViewDebug.ExportedProperty(category="drawing") @ColorInt public int getSolidColor();
+ method @LayoutRes public int getSourceLayoutResId();
method public android.animation.StateListAnimator getStateListAnimator();
method protected int getSuggestedMinimumHeight();
method protected int getSuggestedMinimumWidth();
@@ -51025,6 +51029,7 @@
public class ViewConfiguration {
ctor @Deprecated public ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
+ method @FloatRange(from=1.0) public static float getAmbiguousGestureMultiplier();
method public static long getDefaultActionModeHideDuration();
method public static int getDoubleTapTimeout();
method @Deprecated public static int getEdgeSlop();
diff --git a/api/system-current.txt b/api/system-current.txt
index 43d055b..13f5e63 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1127,6 +1127,7 @@
method public String getNotificationChannelId();
method @Nullable public String getTaskRootClassName();
method @Nullable public String getTaskRootPackageName();
+ method public boolean isInstantApp();
field public static final int NOTIFICATION_INTERRUPTION = 12; // 0xc
field public static final int NOTIFICATION_SEEN = 10; // 0xa
field public static final int SLICE_PINNED = 14; // 0xe
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 427662a..9a6387a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -235,6 +235,7 @@
BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166;
BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
+ ProcessStartTime process_start_time = 169;
}
// Pulled events will start at field 10000.
@@ -5263,6 +5264,7 @@
PERMISSION_IGNORED = 12;
SWIPE_LEFT = 13;
SWIPE_RIGHT = 14;
+ STACK_EXPANDED = 15;
}
optional Action action = 6;
@@ -5303,3 +5305,59 @@
// Describes how many times in a row did the power manager reset the screen off timeout.
optional uint32 consecutive_timeout_extended_count = 1;
}
+
+/*
+* Logs number of milliseconds it takes to start a process.
+* The definition of app process start time is from the app launch time to
+* the time that Zygote finished forking the app process and loaded the
+* application package's java classes.
+
+* This metric is different from AppStartOccurred which is for foreground
+* activity only.
+
+* ProcessStartTime can report all processes (both foreground and background)
+* start time.
+*
+* Logged from:
+* frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+*/
+message ProcessStartTime {
+ // The uid of the ProcessRecord.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process pid.
+ optional int32 pid = 2;
+
+ // The process name.
+ // Usually package name, "system" for system server.
+ // Provided by ActivityManagerService.
+ optional string process_name = 3;
+
+ enum StartType {
+ UNKNOWN = 0;
+ WARM = 1;
+ HOT = 2;
+ COLD = 3;
+ }
+
+ // The start type.
+ optional StartType type = 4;
+
+ // The elapsed realtime at the start of the process.
+ optional int64 process_start_time_millis = 5;
+
+ // Number of milliseconds it takes to reach bind application.
+ optional int32 bind_application_delay_millis = 6;
+
+ // Number of milliseconds it takes to finish start of the process.
+ optional int32 process_start_delay_millis = 7;
+
+ // hostingType field in ProcessRecord, the component type such as "activity",
+ // "service", "content provider", "broadcast" or other strings.
+ optional string hosting_type = 8;
+
+ // hostingNameStr field in ProcessRecord. The component class name that runs
+ // in this process.
+ optional string hosting_name = 9;
+}
+
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 412d7f4..eea0543 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -203,7 +203,7 @@
void unbindFinished(in IBinder token, in Intent service, boolean doRebind);
void setProcessImportant(in IBinder token, int pid, boolean isForeground, String reason);
void setServiceForeground(in ComponentName className, in IBinder token,
- int id, in Notification notification, int flags);
+ int id, in Notification notification, int flags, int foregroundServiceType);
boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
void getMemoryInfo(out ActivityManager.MemoryInfo outInfo);
List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 5fa8526..87bf5ed 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -16,7 +16,10 @@
package android.app;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ComponentCallbacks2;
@@ -685,12 +688,10 @@
* the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
* this API.</p>
*
- * <p>To use this API, apps targeting API {@link android.os.Build.VERSION_CODES#Q} or later must
- * specify the foreground service type using attribute
- * {@link android.R.attr#foregroundServiceType} in service element of manifest file, otherwise
- * a SecurityException is thrown when this API is called. Apps targeting API older than
- * {@link android.os.Build.VERSION_CODES#Q} do not need to specify the foreground service type
- * </p>
+ * <p>Apps built with SDK version {@link android.os.Build.VERSION_CODES#Q} or later can specify
+ * the foreground service types using attribute {@link android.R.attr#foregroundServiceType} in
+ * service element of manifest file. The value of attribute
+ * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
*
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
@@ -703,7 +704,42 @@
try {
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, id,
- notification, 0);
+ notification, 0, FOREGROUND_SERVICE_TYPE_MANIFEST);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * An overloaded version of {@link #startForeground(int, Notification)} with additional
+ * foregroundServiceType parameter.
+ *
+ * <p>Apps built with SDK version {@link android.os.Build.VERSION_CODES#Q} or later can specify
+ * the foreground service types using attribute {@link android.R.attr#foregroundServiceType} in
+ * service element of manifest file. The value of attribute
+ * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
+ *
+ * <p>The foregroundServiceType parameter must be a subset flags of what is specified in manifest
+ * attribute {@link android.R.attr#foregroundServiceType}, if not, an IllegalArgumentException is
+ * thrown. Specify foregroundServiceType parameter as
+ * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that
+ * is specified in manifest attribute foregroundServiceType.</p>
+ *
+ * @param id The identifier for this notification as per
+ * {@link NotificationManager#notify(int, Notification)
+ * NotificationManager.notify(int, Notification)}; must not be 0.
+ * @param notification The Notification to be displayed.
+ * @param foregroundServiceType must be a subset flags of manifest attribute
+ * {@link android.R.attr#foregroundServiceType} flags.
+ * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest
+ * attribute {@link android.R.attr#foregroundServiceType}.
+ * @see {@link android.content.pm.ServiceInfo} for the set of FOREGROUND_SERVICE_TYPE flags.
+ */
+ public final void startForeground(int id, @NonNull Notification notification,
+ int foregroundServiceType) {
+ try {
+ mActivityManager.setServiceForeground(
+ new ComponentName(this, mClassName), mToken, id,
+ notification, 0, foregroundServiceType);
} catch (RemoteException ex) {
}
}
@@ -731,7 +767,8 @@
public final void stopForeground(@StopForegroundFlags int flags) {
try {
mActivityManager.setServiceForeground(
- new ComponentName(this, mClassName), mToken, 0, null, flags);
+ new ComponentName(this, mClassName), mToken, 0, null,
+ flags, 0);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 24d3b98..428c9b0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10591,14 +10591,19 @@
}
/**
- * Returns if a package is whitelisted to access cross-profile calendar APIs.
+ * Returns if a package is allowed to access cross-profile calendar APIs.
+ *
+ * <p>A package is allowed to access cross-profile calendar APIs if it's allowed by
+ * admins via {@link #setCrossProfileCalendarPackages(ComponentName, Set)} and
+ * {@link android.provider.Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED}
+ * is turned on in the managed profile.
*
* <p>To query for a specific user, use
* {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
* that user, and get a {@link DevicePolicyManager} from this context.
*
* @param packageName the name of the package
- * @return {@code true} if the package is whitelisted to access cross-profile calendar APIs.
+ * @return {@code true} if the package is allowed to access cross-profile calendar APIs.
* {@code false} otherwise.
*
* @see #setCrossProfileCalendarPackages(ComponentName, Set)
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 451f44b..8cbe7a3 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -405,6 +405,16 @@
}
/**
+ * Indicates whether it is an instant app.
+ * STOPSHIP b/111407095: Add GTS tests for the newly added API method.
+ * @hide
+ */
+ @SystemApi
+ public boolean isInstantApp() {
+ return (mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == FLAG_IS_PACKAGE_INSTANT_APP;
+ }
+
+ /**
* The class name of the source of this event. This may be null for
* certain events.
*/
@@ -530,7 +540,7 @@
/** @hide */
public Event getObfuscatedIfInstantApp() {
- if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
+ if (!isInstantApp()) {
return this;
}
final Event ret = new Event(this);
@@ -735,6 +745,7 @@
p.writeString(event.mNotificationChannelId);
break;
}
+ p.writeInt(event.mFlags);
}
/**
@@ -802,6 +813,7 @@
eventOut.mNotificationChannelId = p.readString();
break;
}
+ eventOut.mFlags = p.readInt();
}
@Override
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1358bc2..b27c5dc 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -640,19 +640,15 @@
public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;
/**
- * Indicate whether this application prefers code integrity, that is, run only code that is
- * signed. This requires android:extractNativeLibs to be "false", as well as .dex and .so (if
- * any) stored uncompressed inside the APK, which is signed. At run time, the implications
- * include:
- *
- * <ul>
- * <li>ART will JIT the dex code directly from the APK. There may be performance characteristic
- * changes depend on the actual workload.
- * </ul>
+ * Indicates whether this application wants to use the embedded dex in the APK, rather than
+ * extracted or locally compiled variants. This keeps the dex code protected by the APK
+ * signature. Such apps will always run in JIT mode (same when they are first installed), and
+ * the system will never generate ahead-of-time compiled code for them. Depending on the app's
+ * workload, there may be some run time performance change, noteably the cold start time.
*
* @hide
*/
- public static final int PRIVATE_FLAG_PREFER_CODE_INTEGRITY = 1 << 25;
+ public static final int PRIVATE_FLAG_USE_EMBEDDED_DEX = 1 << 25;
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -669,7 +665,7 @@
PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
PRIVATE_FLAG_OEM,
PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
- PRIVATE_FLAG_PREFER_CODE_INTEGRITY,
+ PRIVATE_FLAG_USE_EMBEDDED_DEX,
PRIVATE_FLAG_PRIVILEGED,
PRIVATE_FLAG_PRODUCT,
PRIVATE_FLAG_PRODUCT_SERVICES,
@@ -1962,8 +1958,8 @@
}
/** @hide */
- public boolean isCodeIntegrityPreferred() {
- return (privateFlags & PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0;
+ public boolean isEmbeddedDexUsed() {
+ return (privateFlags & PRIVATE_FLAG_USE_EMBEDDED_DEX) != 0;
}
/**
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4f674bd..8b8f3e5 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1290,6 +1290,28 @@
isStaged = source.readBoolean();
}
+ /** {@hide} */
+ public SessionParams copy() {
+ SessionParams ret = new SessionParams(mode);
+ ret.installFlags = installFlags;
+ ret.installLocation = installLocation;
+ ret.installReason = installReason;
+ ret.sizeBytes = sizeBytes;
+ ret.appPackageName = appPackageName;
+ ret.appIcon = appIcon; // not a copy.
+ ret.appLabel = appLabel;
+ ret.originatingUri = originatingUri; // not a copy, but immutable.
+ ret.originatingUid = originatingUid;
+ ret.referrerUri = referrerUri; // not a copy, but immutable.
+ ret.abiOverride = abiOverride;
+ ret.volumeUuid = volumeUuid;
+ ret.grantedRuntimePermissions = grantedRuntimePermissions;
+ ret.installerPackageName = installerPackageName;
+ ret.isMultiPackage = isMultiPackage;
+ ret.isStaged = isStaged;
+ return ret;
+ }
+
/**
* Check if there are hidden options set.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1fab443..f10f0c6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -475,7 +475,7 @@
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
public final boolean isSplitRequired;
- public final boolean preferCodeIntegrity;
+ public final boolean useEmbeddedDex;
public ApkLite(String codePath, String packageName, String splitName,
boolean isFeatureSplit,
@@ -484,7 +484,7 @@
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
SigningDetails signingDetails, boolean coreApp,
boolean debuggable, boolean multiArch, boolean use32bitAbi,
- boolean preferCodeIntegrity, boolean extractNativeLibs, boolean isolatedSplits) {
+ boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
@@ -501,7 +501,7 @@
this.debuggable = debuggable;
this.multiArch = multiArch;
this.use32bitAbi = use32bitAbi;
- this.preferCodeIntegrity = preferCodeIntegrity;
+ this.useEmbeddedDex = useEmbeddedDex;
this.extractNativeLibs = extractNativeLibs;
this.isolatedSplits = isolatedSplits;
this.isSplitRequired = isSplitRequired;
@@ -1726,7 +1726,7 @@
boolean isolatedSplits = false;
boolean isFeatureSplit = false;
boolean isSplitRequired = false;
- boolean preferCodeIntegrity = false;
+ boolean useEmbeddedDex = false;
String configForSplit = null;
String usesSplitName = null;
@@ -1790,8 +1790,8 @@
extractNativeLibsProvided = Boolean.valueOf(
attrs.getAttributeBooleanValue(i, true));
}
- if ("preferCodeIntegrity".equals(attr)) {
- preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
+ if ("useEmbeddedDex".equals(attr)) {
+ useEmbeddedDex = attrs.getAttributeBooleanValue(i, false);
}
}
} else if (TAG_USES_SPLIT.equals(parser.getName())) {
@@ -1851,16 +1851,10 @@
final boolean extractNativeLibs = (extractNativeLibsProvided != null)
? extractNativeLibsProvided : extractNativeLibsDefault;
- if (preferCodeIntegrity && extractNativeLibs) {
- throw new PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Can't request both preferCodeIntegrity and extractNativeLibs");
- }
-
return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
- multiArch, use32bitAbi, preferCodeIntegrity, extractNativeLibs, isolatedSplits);
+ multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits);
}
/**
@@ -3789,9 +3783,9 @@
}
if (sa.getBoolean(
- R.styleable.AndroidManifestApplication_preferCodeIntegrity,
+ R.styleable.AndroidManifestApplication_useEmbeddedDex,
false)) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY;
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX;
}
if (sa.getBoolean(
@@ -5576,7 +5570,7 @@
s.info.mForegroundServiceType = sa.getInt(
com.android.internal.R.styleable.AndroidManifestService_foregroundServiceType,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED);
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
s.info.flags = 0;
if (sa.getBoolean(
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 8300c0c..60475de 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -101,77 +101,73 @@
/**
* The default foreground service type if not been set in manifest file.
*/
- public static final int FOREGROUND_SERVICE_TYPE_UNSPECIFIED = 0;
+ public static final int FOREGROUND_SERVICE_TYPE_NONE = 0;
/**
- * Constant corresponding to <code>sync</code> in
+ * Constant corresponding to <code>dataSync</code> in
* the {@link android.R.attr#foregroundServiceType} attribute.
* Data(photo, file, account) upload/download, backup/restore, import/export, fetch,
* transfer over network between device and cloud.
*/
- public static final int FOREGROUND_SERVICE_TYPE_SYNC = 1;
+ public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1 << 0;
/**
- * Constant corresponding to <code>mediaPlay</code> in
+ * Constant corresponding to <code>mediaPlayback</code> in
* the {@link android.R.attr#foregroundServiceType} attribute.
- * Music, video, news or other media play.
+ * Music, video, news or other media playback.
*/
- public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAY = 2;
+ public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 1 << 1;
/**
* Constant corresponding to <code>phoneCall</code> in
* the {@link android.R.attr#foregroundServiceType} attribute.
* Ongoing phone call or video conference.
*/
- public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 3;
+ public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = 1 << 2;
/**
* Constant corresponding to <code>location</code> in
* the {@link android.R.attr#foregroundServiceType} attribute.
* GPS, map, navigation location update.
*/
- public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 4;
+ public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 1 << 3;
/**
- * Constant corresponding to <code>deviceCompanion</code> in
+ * Constant corresponding to <code>connectedDevice</code> in
* the {@link android.R.attr#foregroundServiceType} attribute.
* Auto, bluetooth, TV or other devices connection, monitoring and interaction.
*/
- public static final int FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION = 5;
+ public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 1 << 4;
/**
- * Constant corresponding to <code>ongoingProcess</code> in
- * the {@link android.R.attr#foregroundServiceType} attribute.
- * Process that should not be interrupted, including installation, setup, photo
- * compression etc.
+ * A special value indicates to use all types set in manifest file.
*/
- public static final int FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS = 6;
+ public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1;
/**
- * The enumeration of values for foreground service type.
+ * The set of flags for foreground service type.
* The foreground service type is set in {@link android.R.attr#foregroundServiceType}
* attribute.
* @hide
*/
- @IntDef(flag = false, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
- FOREGROUND_SERVICE_TYPE_UNSPECIFIED,
- FOREGROUND_SERVICE_TYPE_SYNC,
- FOREGROUND_SERVICE_TYPE_MEDIA_PLAY,
+ @IntDef(flag = true, prefix = { "FOREGROUND_SERVICE_TYPE_" }, value = {
+ FOREGROUND_SERVICE_TYPE_NONE,
+ FOREGROUND_SERVICE_TYPE_DATA_SYNC,
+ FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
FOREGROUND_SERVICE_TYPE_PHONE_CALL,
FOREGROUND_SERVICE_TYPE_LOCATION,
- FOREGROUND_SERVICE_TYPE_DEVICE_COMPANION,
- FOREGROUND_SERVICE_TYPE_ONGOING_PROCESS
+ FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ForegroundServiceType {}
/**
* The type of foreground service, set in
- * {@link android.R.attr#foregroundServiceType} attribute, one value in
+ * {@link android.R.attr#foregroundServiceType} attribute by ORing flags in
* {@link ForegroundServiceType}
* @hide
*/
- public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_UNSPECIFIED;
+ public @ForegroundServiceType int mForegroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
public ServiceInfo() {
}
@@ -217,6 +213,7 @@
super.writeToParcel(dest, parcelableFlags);
dest.writeString(permission);
dest.writeInt(flags);
+ dest.writeInt(mForegroundServiceType);
}
public static final Creator<ServiceInfo> CREATOR =
@@ -233,5 +230,6 @@
super(source);
permission = source.readString();
flags = source.readInt();
+ mForegroundServiceType = source.readInt();
}
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index baf64ad..59db49e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1394,6 +1394,17 @@
mResourcesImpl.getValue(name, outValue, resolveRefs);
}
+
+ /**
+ * @param set AttributeSet for which we want to find the source.
+ * @return The resource id for the source that is backing the given AttributeSet
+ * @hide
+ */
+ @AnyRes
+ public static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+ return ResourcesImpl.getAttributeSetSourceResId(set);
+ }
+
/**
* This class holds the current attribute values for a particular theme.
* In other words, a Theme is a set of values for resource attributes;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index d8564d5..9898079 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -15,6 +15,8 @@
*/
package android.content.res;
+import static android.content.res.Resources.ID_NULL;
+
import android.animation.Animator;
import android.animation.StateListAnimator;
import android.annotation.AnyRes;
@@ -1222,7 +1224,7 @@
for (int i = 0; i < num; i++) {
if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
&& cachedXmlBlockFiles[i].equals(file)) {
- return cachedXmlBlocks[i].newParser();
+ return cachedXmlBlocks[i].newParser(id);
}
}
@@ -1239,7 +1241,7 @@
cachedXmlBlockCookies[pos] = assetCookie;
cachedXmlBlockFiles[pos] = file;
cachedXmlBlocks[pos] = block;
- return block.newParser();
+ return block.newParser(id);
}
}
} catch (Exception e) {
@@ -1299,6 +1301,14 @@
}
}
+ @AnyRes
+ static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
+ if (set == null) {
+ return ID_NULL;
+ }
+ return ((XmlBlock.Parser) set).getSourceResId();
+ }
+
LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
return sPreloadedDrawables[0];
}
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index 4e1159a..d8c3dde 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -16,6 +16,9 @@
package android.content.res;
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.AnyRes;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.util.TypedValue;
@@ -73,19 +76,29 @@
@UnsupportedAppUsage
public XmlResourceParser newParser() {
+ return newParser(ID_NULL);
+ }
+
+ public XmlResourceParser newParser(@AnyRes int resId) {
synchronized (this) {
if (mNative != 0) {
- return new Parser(nativeCreateParseState(mNative), this);
+ return new Parser(nativeCreateParseState(mNative), this, resId);
}
return null;
}
}
/*package*/ final class Parser implements XmlResourceParser {
- Parser(long parseState, XmlBlock block) {
+ Parser(long parseState, XmlBlock block, @AnyRes int sourceResId) {
mParseState = parseState;
mBlock = block;
block.mOpenCount++;
+ mSourceResId = sourceResId;
+ }
+
+ @AnyRes
+ public int getSourceResId() {
+ return mSourceResId;
}
public void setFeature(String name, boolean state) throws XmlPullParserException {
@@ -473,6 +486,7 @@
private boolean mDecNextDepth = false;
private int mDepth = 0;
private int mEventType = START_DOCUMENT;
+ private @AnyRes int mSourceResId;
}
protected void finalize() throws Throwable {
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 5afe1a9..d257c03 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
@@ -29,6 +30,7 @@
/**
* A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
*/
+@SystemService(Context.BIOMETRIC_SERVICE)
public class BiometricManager {
private static final String TAG = "BiometricManager";
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 7d9ec70..c794a69 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -237,16 +237,6 @@
private static final int LONG_PRESS = 2;
private static final int TAP = 3;
- /**
- * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
- * scrolling, will be inhibited. However, to account for the possibility of incorrect
- * classification, the default scrolling will only be inhibited if the gesture moves beyond
- * (default touch slop * AMBIGUOUS_GESTURE_MULTIPLIER). Likewise, the default long press
- * timeout will be increased for some situations where the default behaviour
- * is to cancel it.
- */
- private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
-
private final Handler mHandler;
@UnsupportedAppUsage
private final OnGestureListener mListener;
@@ -636,6 +626,7 @@
hasPendingLongPress && ambiguousGesture;
if (shouldInhibitDefaultAction) {
// Inhibit default long press
+ final float multiplier = ViewConfiguration.getAmbiguousGestureMultiplier();
if (distance > slopSquare) {
// The default action here is to remove long press. But if the touch
// slop below gets increased, and we never exceed the modified touch
@@ -643,15 +634,15 @@
// will happen in response to user input. To prevent this,
// reschedule long press with a modified timeout.
mHandler.removeMessages(LONG_PRESS);
- final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
+ final long longPressTimeout = ViewConfiguration.getLongPressTimeout();
mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime()
- + longPressTimeout * AMBIGUOUS_GESTURE_MULTIPLIER);
+ + (long) (longPressTimeout * multiplier));
}
// Inhibit default scroll. If a gesture is ambiguous, we prevent scroll
// until the gesture is resolved.
// However, for safety, simply increase the touch slop in case the
// classification is erroneous. Since the value is squared, multiply twice.
- slopSquare *= AMBIGUOUS_GESTURE_MULTIPLIER * AMBIGUOUS_GESTURE_MULTIPLIER;
+ slopSquare *= multiplier * multiplier;
}
if (distance > slopSquare) {
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 6061cb2..6a290b7 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -414,8 +414,7 @@
// Make sure the application allows code generation
ApplicationInfo appInfo = mContext.getApplicationInfo();
- if ((appInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
- || appInfo.isPrivilegedApp()) {
+ if (appInfo.isEmbeddedDexUsed() || appInfo.isPrivilegedApp()) {
mUseCompiledView = false;
return;
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 9d3552f..b6a4a09 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -431,8 +431,9 @@
/**
* This flag indicates that the window that received this motion event is partly
- * or wholly obscured by another visible window above it. This flag is set to true
- * even if the event did not directly pass through the obscured area.
+ * or wholly obscured by another visible window above it. This flag is set to true
+ * if the event directly passed through the obscured area.
+ *
* A security sensitive application can check this flag to identify situations in which
* a malicious application may have covered up part of its content for the purpose
* of misleading the user or hijacking touches. An appropriate response might be
@@ -443,16 +444,17 @@
/**
* This flag indicates that the window that received this motion event is partly
- * or wholly obscured by another visible window above it. This flag is set to true
+ * or wholly obscured by another visible window above it. This flag is set to true
* even if the event did not directly pass through the obscured area.
+ *
* A security sensitive application can check this flag to identify situations in which
* a malicious application may have covered up part of its content for the purpose
* of misleading the user or hijacking touches. An appropriate response might be
* to drop the suspect touches or to take additional precautions to confirm the user's
* actual intent.
*
- * Unlike FLAG_WINDOW_IS_OBSCURED, this is actually true.
- * @hide
+ * Unlike FLAG_WINDOW_IS_OBSCURED, this is true even if the window that received this event is
+ * obstructed in areas other than the touched location.
*/
public static final int FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6e76647..8061cc3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -155,6 +155,8 @@
private static native int nativeGetActiveConfig(IBinder displayToken);
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
+ private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
+ IBinder displayToken);
private static native int[] nativeGetCompositionDataspaces();
private static native int nativeGetActiveColorMode(IBinder displayToken);
private static native boolean nativeSetActiveColorMode(IBinder displayToken,
@@ -1536,6 +1538,73 @@
}
/**
+ * Color coordinates in CIE1931 XYZ color space
+ *
+ * @hide
+ */
+ public static final class CieXyz {
+ /**
+ * @hide
+ */
+ public float X;
+
+ /**
+ * @hide
+ */
+ public float Y;
+
+ /**
+ * @hide
+ */
+ public float Z;
+ }
+
+ /**
+ * Contains a display's color primaries
+ *
+ * @hide
+ */
+ public static final class DisplayPrimaries {
+ /**
+ * @hide
+ */
+ public CieXyz red;
+
+ /**
+ * @hide
+ */
+ public CieXyz green;
+
+ /**
+ * @hide
+ */
+ public CieXyz blue;
+
+ /**
+ * @hide
+ */
+ public CieXyz white;
+
+ /**
+ * @hide
+ */
+ public DisplayPrimaries() {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(
+ IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeGetDisplayNativePrimaries(displayToken);
+ }
+
+ /**
* @hide
*/
public static int getActiveColorMode(IBinder displayToken) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 519181d..992b996 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.content.res.Resources.ID_NULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
@@ -3985,15 +3986,6 @@
public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
/**
- * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain the default
- * long press action will be inhibited. However, to account for the possibility of incorrect
- * classification, the default long press timeout will instead be increased for some situations
- * by the following factor.
- * Likewise, the touch slop for allowing long press will be increased when gesture is uncertain.
- */
- private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
-
- /**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -5052,6 +5044,9 @@
@Nullable
private WeakReference<ContentCaptureSession> mContentCaptureSession;
+ @LayoutRes
+ private int mSourceLayoutId = ID_NULL;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -5217,6 +5212,8 @@
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context);
+ mSourceLayoutId = Resources.getAttributeSetSourceResId(attrs);
+
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
@@ -14801,18 +14798,20 @@
motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
int touchSlop = mTouchSlop;
if (ambiguousGesture && hasPendingLongPressCallback()) {
+ final float ambiguousMultiplier =
+ ViewConfiguration.getAmbiguousGestureMultiplier();
if (!pointInView(x, y, touchSlop)) {
// The default action here is to cancel long press. But instead, we
// just extend the timeout here, in case the classification
// stays ambiguous.
removeLongPressCallback();
- long delay = ViewConfiguration.getLongPressTimeout()
- * AMBIGUOUS_GESTURE_MULTIPLIER;
+ long delay = (long) (ViewConfiguration.getLongPressTimeout()
+ * ambiguousMultiplier);
// Subtract the time already spent
delay -= event.getEventTime() - event.getDownTime();
checkForLongClick(delay, x, y);
}
- touchSlop *= AMBIGUOUS_GESTURE_MULTIPLIER;
+ touchSlop *= ambiguousMultiplier;
}
// Be lenient about moving outside of buttons
@@ -23253,6 +23252,18 @@
}
/**
+ * A {@link View} can be inflated from an XML layout. For such Views this method returns the
+ * resource ID of the source layout.
+ *
+ * @return The layout resource id if this view was inflated from XML, otherwise
+ * {@link Resources#ID_NULL}.
+ */
+ @LayoutRes
+ public int getSourceLayoutResId() {
+ return mSourceLayoutId;
+ }
+
+ /**
* Returns the top padding of this view.
*
* @return the top padding in pixels
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index d03d97e..94d1b6d 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FloatRange;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.AppGlobals;
@@ -286,6 +287,11 @@
private static final int HAS_PERMANENT_MENU_KEY_TRUE = 1;
private static final int HAS_PERMANENT_MENU_KEY_FALSE = 2;
+ /**
+ * The multiplication factor for inhibiting default gestures.
+ */
+ private static final float AMBIGUOUS_GESTURE_MULTIPLIER = 2f;
+
private final int mEdgeSlop;
private final int mFadingEdgeLength;
private final int mMinimumFlingVelocity;
@@ -911,6 +917,22 @@
}
/**
+ * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
+ * scrolling, will be inhibited.
+ * However, to account for the possibility of incorrect classification,
+ * the default scrolling will only be inhibited if the pointer travels less than
+ * (getScaledTouchSlop() * this factor).
+ * Likewise, the default long press timeout will be increased by this factor for some situations
+ * where the default behaviour is to cancel it.
+ *
+ * @return The multiplication factor for inhibiting default gestures.
+ */
+ @FloatRange(from = 1.0)
+ public static float getAmbiguousGestureMultiplier() {
+ return AMBIGUOUS_GESTURE_MULTIPLIER;
+ }
+
+ /**
* Report if the device has a permanent menu key available to the user.
*
* <p>As of Android 3.0, devices may not have a permanent menu key available.
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index fde0ced..f31856c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -67,9 +67,6 @@
private final Object mLock = new Object();
- @GuardedBy("mLock")
- private boolean mDisabled;
-
@NonNull
private final Context mContext;
@@ -115,8 +112,7 @@
public MainContentCaptureSession getMainContentCaptureSession() {
synchronized (mLock) {
if (mMainSession == null) {
- mMainSession = new MainContentCaptureSession(mContext, mHandler, mService,
- mDisabled);
+ mMainSession = new MainContentCaptureSession(mContext, this, mHandler, mService);
if (VERBOSE) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
}
return mMainSession;
@@ -180,9 +176,17 @@
* </ul>
*/
public boolean isContentCaptureEnabled() {
+ if (mService == null) return false;
+
+ final MainContentCaptureSession mainSession;
synchronized (mLock) {
- return mService != null && !mDisabled;
+ mainSession = mMainSession;
}
+ // The main session is only set when the activity starts, so we need to return true until
+ // then.
+ if (mainSession != null && mainSession.isDisabled()) return false;
+
+ return true;
}
/**
@@ -287,7 +291,8 @@
public void dump(String prefix, PrintWriter pw) {
synchronized (mLock) {
pw.print(prefix); pw.println("ContentCaptureManager");
- pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+ pw.print(prefix); pw.print("isContentCaptureEnabled(): ");
+ pw.println(isContentCaptureEnabled());
pw.print(prefix); pw.print("Context: "); pw.println(mContext);
pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId());
if (mService != null) {
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 2eca51f..034c8fa 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -89,21 +89,23 @@
*/
public static final String EXTRA_BINDER = "binder";
- // TODO(b/111276913): make sure disabled state is in sync with manager's disabled
@NonNull
- private final AtomicBoolean mDisabled;
+ private final AtomicBoolean mDisabled = new AtomicBoolean(false);
@NonNull
private final Context mContext;
@NonNull
+ private final ContentCaptureManager mManager;
+
+ @NonNull
private final Handler mHandler;
/**
* Interface to the system_server binder object - it's only used to start the session (and
* notify when the session is finished).
*/
- @Nullable
+ @Nullable // TODO(b/122959591): shoul never be null, we should make main session null instead
private final IContentCaptureManager mSystemServerInterface;
/**
@@ -136,13 +138,13 @@
private final LocalLog mFlushHistory = new LocalLog(10);
/** @hide */
- protected MainContentCaptureSession(@NonNull Context context, @NonNull Handler handler,
- @Nullable IContentCaptureManager systemServerInterface,
- @NonNull boolean disabled) {
+ protected MainContentCaptureSession(@NonNull Context context,
+ @NonNull ContentCaptureManager manager, @NonNull Handler handler,
+ @Nullable IContentCaptureManager systemServerInterface) {
mContext = context;
+ mManager = manager;
mHandler = handler;
mSystemServerInterface = systemServerInterface;
- mDisabled = new AtomicBoolean(disabled);
}
@Override
@@ -235,8 +237,8 @@
/**
* Callback from {@code system_server} after call to
- * {@link IContentCaptureManager#startSession(int, IBinder, ComponentName, String,
- * int, IResultReceiver)}.
+ * {@link IContentCaptureManager#startSession(IBinder, ComponentName, String, int,
+ * IResultReceiver)}
*
* @param resultCode session state
* @param binder handle to {@code IContentCaptureDirectManager}
@@ -517,8 +519,12 @@
@Override
boolean isContentCaptureEnabled() {
- return super.isContentCaptureEnabled() && mSystemServerInterface != null
- && !mDisabled.get();
+ return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+ }
+
+ // Called by ContentCaptureManager.isContentCaptureEnabled
+ boolean isDisabled() {
+ return mDisabled.get();
}
// TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c38566b..e9d72a2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4489,6 +4489,13 @@
// when selecting text when the handles jump to the end / start of words which may be on
// a different line.
protected int mPreviousLineTouched = UNSET_LINE;
+ // The raw x coordinate of the motion down event which started the current dragging session.
+ // Only used and stored when magnifier is used.
+ private float mCurrentDragInitialTouchRawX = UNSET_X_VALUE;
+ // The scale transform applied by containers to the TextView. Only used and computed
+ // when magnifier is used.
+ private float mTextViewScaleX;
+ private float mTextViewScaleY;
private HandleView(Drawable drawableLtr, Drawable drawableRtl, final int id) {
super(mTextView.getContext());
@@ -4808,25 +4815,44 @@
/ mMagnifierAnimator.mMagnifier.getZoom());
final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
- return glyphHeight > magnifierContentHeight;
+ return glyphHeight * mTextViewScaleY > magnifierContentHeight;
}
- private boolean viewIsMatrixTransformed() {
+ /**
+ * Traverses the hierarchy above the text view, and computes the total scale applied
+ * to it. If a rotation is encountered, the method returns {@code false}, indicating
+ * that the magnifier should not be shown anyways. It would be nice to keep these two
+ * pieces of logic separate (the rotation check and the total scale calculation),
+ * but for efficiency we can do them in a single go.
+ * @return whether the text view is rotated
+ */
+ private boolean checkForTransforms() {
if (mMagnifierAnimator.mMagnifierIsShowing) {
// Do not check again when the magnifier is currently showing.
- return false;
- }
- if (!mTextView.hasIdentityMatrix()) {
return true;
}
+
+ if (mTextView.getRotation() != 0f || mTextView.getRotationX() != 0f
+ || mTextView.getRotationY() != 0f) {
+ return false;
+ }
+ mTextViewScaleX = mTextView.getScaleX();
+ mTextViewScaleY = mTextView.getScaleY();
+
ViewParent viewParent = mTextView.getParent();
while (viewParent != null) {
- if (viewParent instanceof View && !((View) viewParent).hasIdentityMatrix()) {
- return true;
+ if (viewParent instanceof View) {
+ final View view = (View) viewParent;
+ if (view.getRotation() != 0f || view.getRotationX() != 0f
+ || view.getRotationY() != 0f) {
+ return false;
+ }
+ mTextViewScaleX *= view.getScaleX();
+ mTextViewScaleY *= view.getScaleY();
}
viewParent = viewParent.getParent();
}
- return false;
+ return true;
}
/**
@@ -4864,6 +4890,12 @@
return false;
}
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mCurrentDragInitialTouchRawX = event.getRawX();
+ } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mCurrentDragInitialTouchRawX = UNSET_X_VALUE;
+ }
+
final Layout layout = mTextView.getLayout();
final int lineNumber = layout.getLineForOffset(offset);
// Compute whether the selection handles are currently on the same line, and,
@@ -4891,6 +4923,8 @@
} else {
rightBound += mTextView.getLayout().getLineRight(lineNumber);
}
+ leftBound *= mTextViewScaleX;
+ rightBound *= mTextViewScaleX;
final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth()
/ mMagnifierAnimator.mMagnifier.getZoom());
if (touchXInView < leftBound - contentWidth / 2
@@ -4898,13 +4932,27 @@
// The touch is too far from the current line / selection, so hide the magnifier.
return false;
}
- showPosInView.x = Math.max(leftBound, Math.min(rightBound, touchXInView));
+
+ final float scaledTouchXInView;
+ if (mTextViewScaleX == 1f) {
+ // In the common case, do not use mCurrentDragInitialTouchRawX to compute this
+ // coordinate, although the formula on the else branch should be equivalent.
+ // Since the formula relies on mCurrentDragInitialTouchRawX being set on
+ // MotionEvent.ACTION_DOWN, this makes us more defensive against cases when
+ // the sequence of events might not look as expected: for example, a sequence of
+ // ACTION_MOVE not preceded by ACTION_DOWN.
+ scaledTouchXInView = touchXInView;
+ } else {
+ scaledTouchXInView = (event.getRawX() - mCurrentDragInitialTouchRawX)
+ * mTextViewScaleX + mCurrentDragInitialTouchRawX
+ - textViewLocationOnScreen[0];
+ }
+ showPosInView.x = Math.max(leftBound, Math.min(rightBound, scaledTouchXInView));
// Vertically snap to middle of current line.
- showPosInView.y = (mTextView.getLayout().getLineTop(lineNumber)
+ showPosInView.y = ((mTextView.getLayout().getLineTop(lineNumber)
+ mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f
- + mTextView.getTotalPaddingTop() - mTextView.getScrollY();
-
+ + mTextView.getTotalPaddingTop() - mTextView.getScrollY()) * mTextViewScaleY;
return true;
}
@@ -4956,8 +5004,8 @@
}
final PointF showPosInView = new PointF();
- final boolean shouldShow = !tooLargeTextForMagnifier()
- && !viewIsMatrixTransformed()
+ final boolean shouldShow = checkForTransforms() /*check not rotated and compute scale*/
+ && !tooLargeTextForMagnifier()
&& obtainMagnifierShowCoordinates(event, showPosInView);
if (shouldShow) {
// Make the cursor visible and stop blinking.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 46f42f7..cfe2939 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -25,6 +25,7 @@
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ClipData;
+import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -80,11 +81,13 @@
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Space;
import android.widget.TextView;
+import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -350,6 +353,50 @@
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
+ Button copyButton = findViewById(R.id.copy_button);
+ copyButton.setOnClickListener(view -> {
+ Intent targetIntent = getTargetIntent();
+ if (targetIntent == null) {
+ finish();
+ } else {
+ final String action = targetIntent.getAction();
+
+ ClipData clipData = null;
+ if (Intent.ACTION_SEND.equals(action)) {
+ String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT);
+ Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+
+ if (extraText != null) {
+ clipData = ClipData.newPlainText(null, extraText);
+ } else if (extraStream != null) {
+ clipData = ClipData.newUri(getContentResolver(), null, extraStream);
+ } else {
+ Log.w(TAG, "No data available to copy to clipboard");
+ return;
+ }
+ } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
+ final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra(
+ Intent.EXTRA_STREAM);
+ clipData = ClipData.newUri(getContentResolver(), null, streams.get(0));
+ for (int i = 1; i < streams.size(); i++) {
+ clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i)));
+ }
+ } else {
+ // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE
+ // so warn about unexpected action
+ Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard");
+ return;
+ }
+
+ ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ clipboardManager.setPrimaryClip(clipData);
+ Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
+
+ finish();
+ }
+ });
+
MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
mChooserShownTime = System.currentTimeMillis();
@@ -414,39 +461,39 @@
private void showDefaultContentPreview(final ViewGroup parentLayout,
final Intent targetIntent) {
CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
- TextView previewTextView = findViewById(R.id.content_preview_text);
if (sharingText == null) {
- previewTextView.setVisibility(View.GONE);
+ findViewById(R.id.content_preview_text_layout).setVisibility(View.GONE);
} else {
- previewTextView.setText(sharingText);
+ TextView textView = findViewById(R.id.content_preview_text);
+ textView.setText(sharingText);
}
String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
- TextView previewTitleView = findViewById(R.id.content_preview_title);
- if (previewTitle == null) {
- previewTitleView.setVisibility(View.GONE);
+ if (previewTitle == null || previewTitle.trim().isEmpty()) {
+ findViewById(R.id.content_preview_title_layout).setVisibility(View.GONE);
} else {
+ TextView previewTitleView = findViewById(R.id.content_preview_title);
previewTitleView.setText(previewTitle);
- }
- ClipData previewData = targetIntent.getClipData();
- Uri previewThumbnail = null;
- if (previewData != null) {
- if (previewData.getItemCount() > 0) {
- ClipData.Item previewDataItem = previewData.getItemAt(0);
- previewThumbnail = previewDataItem.getUri();
+ ClipData previewData = targetIntent.getClipData();
+ Uri previewThumbnail = null;
+ if (previewData != null) {
+ if (previewData.getItemCount() > 0) {
+ ClipData.Item previewDataItem = previewData.getItemAt(0);
+ previewThumbnail = previewDataItem.getUri();
+ }
}
- }
- ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
- if (previewThumbnail == null) {
- previewThumbnailView.setVisibility(View.GONE);
- } else {
- Bitmap bmp = loadThumbnail(previewThumbnail, new Size(200, 200));
- if (bmp == null) {
+ ImageView previewThumbnailView = findViewById(R.id.content_preview_thumbnail);
+ if (previewThumbnail == null) {
previewThumbnailView.setVisibility(View.GONE);
} else {
- previewThumbnailView.setImageBitmap(bmp);
+ Bitmap bmp = loadThumbnail(previewThumbnail, new Size(100, 100));
+ if (bmp == null) {
+ previewThumbnailView.setVisibility(View.GONE);
+ } else {
+ previewThumbnailView.setImageBitmap(bmp);
+ }
}
}
}
@@ -2020,8 +2067,8 @@
private void updatePath(int width, int height) {
mPath.reset();
- int imageWidth = width - getPaddingLeft() - getPaddingRight();
- int imageHeight = height - getPaddingTop() - getPaddingBottom();
+ int imageWidth = width - getPaddingRight();
+ int imageHeight = height - getPaddingBottom();
mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius,
mRadius, Path.Direction.CW);
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 474d4d7..b881aef 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -94,6 +94,11 @@
*/
public static final int PROFILE_SYSTEM_SERVER = 1 << 14;
+ /**
+ * Enable profiling from shell.
+ */
+ public static final int PROFILE_FROM_SHELL = 1 << 15;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 3f9ec45..79bfa17 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -15,6 +15,7 @@
#include "Utils.h"
#include "core_jni_helpers.h"
+#include <HardwareBitmapUploader.h>
#include <nativehelper/JNIHelp.h>
#include <androidfw/Asset.h>
#include <androidfw/ResourceTypes.h>
@@ -278,6 +279,11 @@
// Set the decode colorType
SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
+ if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+ !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+ decodeColorType = kN32_SkColorType;
+ }
+
sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
decodeColorType, prefColorSpace);
@@ -354,13 +360,7 @@
const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
decodeColorType, alphaType, decodeColorSpace);
- // For wide gamut images, we will leave the color space on the SkBitmap. Otherwise,
- // use the default.
SkImageInfo bitmapInfo = decodeInfo;
- if (decodeInfo.colorSpace() && decodeInfo.colorSpace()->isSRGB()) {
- bitmapInfo = bitmapInfo.makeColorSpace(decodeInfo.refColorSpace());
- }
-
if (decodeColorType == kGray_8_SkColorType) {
// The legacy implementation of BitmapFactory used kAlpha8 for
// grayscale images (before kGray8 existed). While the codec
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index b4ba749..d65f324 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -33,6 +33,7 @@
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
+#include <HardwareBitmapUploader.h>
#include <nativehelper/JNIHelp.h>
#include <androidfw/Asset.h>
#include <binder/Parcel.h>
@@ -166,6 +167,10 @@
SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
SkColorType decodeColorType = brd->computeOutputColorType(colorType);
+ if (decodeColorType == kRGBA_F16_SkColorType && isHardware &&
+ !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+ decodeColorType = kN32_SkColorType;
+ }
// Set up the pixel allocator
SkBRDAllocator* allocator = nullptr;
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 72e14e7..2d83ac3 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -24,6 +24,7 @@
#include "core_jni_helpers.h"
#include <hwui/Bitmap.h>
+#include <HardwareBitmapUploader.h>
#include <SkAndroidCodec.h>
#include <SkEncodedImageFormat.h>
@@ -256,6 +257,17 @@
// This is currently the only way to know that we should decode to F16.
colorType = codec->computeOutputColorType(colorType);
}
+
+ const bool isHardware = !requireMutable
+ && (allocator == ImageDecoder::kDefault_Allocator ||
+ allocator == ImageDecoder::kHardware_Allocator)
+ && colorType != kGray_8_SkColorType;
+
+ if (colorType == kRGBA_F16_SkColorType && isHardware &&
+ !uirenderer::HardwareBitmapUploader::hasFP16Support()) {
+ colorType = kN32_SkColorType;
+ }
+
sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
@@ -449,10 +461,7 @@
if (requireMutable) {
bitmapCreateFlags |= bitmap::kBitmapCreateFlag_Mutable;
} else {
- if ((allocator == ImageDecoder::kDefault_Allocator ||
- allocator == ImageDecoder::kHardware_Allocator)
- && bm.colorType() != kAlpha_8_SkColorType)
- {
+ if (isHardware) {
sk_sp<Bitmap> hwBitmap = Bitmap::allocateHardwareBitmap(bm);
if (hwBitmap) {
hwBitmap->setImmutable();
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index e741979..298f7f8 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -106,7 +106,7 @@
static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
- jfloatArray posArray, jint tileMode, long colorSpaceHandle) {
+ jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
SkPoint pts[2];
pts[0].set(x0, y0);
pts[1].set(x1, y1);
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 1ab5843..b9301d4 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "InputApplicationHandle"
#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
@@ -63,16 +64,7 @@
mInfo = new InputApplicationInfo();
}
- jstring nameObj = jstring(env->GetObjectField(obj,
- gInputApplicationHandleClassInfo.name));
- if (nameObj) {
- const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
- mInfo->name = nameStr;
- env->ReleaseStringUTFChars(nameObj, nameStr);
- env->DeleteLocalRef(nameObj);
- } else {
- mInfo->name = "<null>";
- }
+ mInfo->name = getStringField(env, obj, gInputApplicationHandleClassInfo.name, "<null>");
mInfo->dispatchingTimeout = env->GetLongField(obj,
gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 67a7441..eb71052 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "InputWindowHandle"
#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
@@ -86,24 +87,14 @@
mInfo.touchableRegion.clear();
- jobject tokenObj = env->GetObjectField(obj,
- gInputWindowHandleClassInfo.token);
+ jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token);
if (tokenObj) {
mInfo.token = ibinderForJavaObject(env, tokenObj);
} else {
mInfo.token.clear();
}
- jstring nameObj = jstring(env->GetObjectField(obj,
- gInputWindowHandleClassInfo.name));
- if (nameObj) {
- const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
- mInfo.name = nameStr;
- env->ReleaseStringUTFChars(nameObj, nameStr);
- env->DeleteLocalRef(nameObj);
- } else {
- mInfo.name = "<null>";
- }
+ mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
mInfo.layoutParamsFlags = env->GetIntField(obj,
gInputWindowHandleClassInfo.layoutParamsFlags);
@@ -241,8 +232,7 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
"ptr", "J");
- GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle,
- clazz,
+ GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, clazz,
"inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index fb6be6b..9819d9a 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "InputChannel-JNI"
#include <nativehelper/JNIHelp.h>
-
+#include "nativehelper/scoped_utf_chars.h"
#include <android_runtime/AndroidRuntime.h>
#include <binder/Parcel.h>
#include <utils/Log.h>
@@ -123,9 +123,8 @@
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
- const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
- std::string name = nameChars;
- env->ReleaseStringUTFChars(nameObj, nameChars);
+ ScopedUtfChars nameChars(env, nameObj);
+ std::string name = nameChars.c_str();
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 0d75de9..ce5512b 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -507,15 +507,15 @@
jmethodID gPositionListener_PositionLostMethod;
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
- jlong renderNodePtr, jobject surfaceview) {
- class SurfaceViewPositionUpdater : public RenderNode::PositionListener {
+ jlong renderNodePtr, jobject listener) {
+ class PositionListenerTrampoline : public RenderNode::PositionListener {
public:
- SurfaceViewPositionUpdater(JNIEnv* env, jobject surfaceview) {
+ PositionListenerTrampoline(JNIEnv* env, jobject listener) {
env->GetJavaVM(&mVm);
- mWeakRef = env->NewWeakGlobalRef(surfaceview);
+ mWeakRef = env->NewWeakGlobalRef(listener);
}
- virtual ~SurfaceViewPositionUpdater() {
+ virtual ~PositionListenerTrampoline() {
jnienv()->DeleteWeakGlobalRef(mWeakRef);
mWeakRef = nullptr;
}
@@ -539,9 +539,14 @@
bounds.roundOut();
}
+ if (mPreviousPosition == bounds) {
+ return;
+ }
+ mPreviousPosition = bounds;
+
incStrong(0);
auto functor = std::bind(
- std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this,
+ std::mem_fn(&PositionListenerTrampoline::doUpdatePositionAsync), this,
(jlong) info.canvasContext.getFrameNumber(),
(jint) bounds.left, (jint) bounds.top,
(jint) bounds.right, (jint) bounds.bottom);
@@ -552,6 +557,11 @@
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
+ if (mPreviousPosition.isEmpty()) {
+ return;
+ }
+ mPreviousPosition.setEmpty();
+
ATRACE_NAME("SurfaceView position lost");
JNIEnv* env = jnienv();
jobject localref = env->NewLocalRef(mWeakRef);
@@ -561,6 +571,7 @@
return;
}
+ // TODO: Remember why this is synchronous and then make a comment
env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
info ? info->canvasContext.getFrameNumber() : 0);
env->DeleteLocalRef(localref);
@@ -596,10 +607,11 @@
JavaVM* mVm;
jobject mWeakRef;
+ uirenderer::Rect mPreviousPosition;
};
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->setPositionListener(new SurfaceViewPositionUpdater(env, surfaceview));
+ renderNode->setPositionListener(new PositionListenerTrampoline(env, listener));
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f1b259e..fad2fe0 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -36,6 +36,7 @@
#include <memory>
#include <stdio.h>
#include <system/graphics.h>
+#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayInfo.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -108,6 +109,23 @@
jmethodID ctor;
} gDisplayedContentSamplingAttributesClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+ jfieldID X;
+ jfieldID Y;
+ jfieldID Z;
+} gCieXyzClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+ jfieldID red;
+ jfieldID green;
+ jfieldID blue;
+ jfieldID white;
+} gDisplayPrimariesClassInfo;
+
// ----------------------------------------------------------------------------
static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -688,6 +706,66 @@
return colorModesArray;
}
+static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return NULL;
+
+ ui::DisplayPrimaries primaries;
+ if (SurfaceComposerClient::getDisplayNativePrimaries(token, primaries) != NO_ERROR) {
+ return NULL;
+ }
+
+ jobject jred = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+ if (jred == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ jobject jgreen = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+ if (jgreen == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ jobject jblue = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+ if (jblue == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ jobject jwhite = env->NewObject(gCieXyzClassInfo.clazz, gCieXyzClassInfo.ctor);
+ if (jwhite == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ jobject jprimaries = env->NewObject(gDisplayPrimariesClassInfo.clazz,
+ gDisplayPrimariesClassInfo.ctor);
+ if (jprimaries == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ env->SetFloatField(jred, gCieXyzClassInfo.X, primaries.red.X);
+ env->SetFloatField(jred, gCieXyzClassInfo.Y, primaries.red.Y);
+ env->SetFloatField(jred, gCieXyzClassInfo.Z, primaries.red.Z);
+ env->SetFloatField(jgreen, gCieXyzClassInfo.X, primaries.green.X);
+ env->SetFloatField(jgreen, gCieXyzClassInfo.Y, primaries.green.Y);
+ env->SetFloatField(jgreen, gCieXyzClassInfo.Z, primaries.green.Z);
+ env->SetFloatField(jblue, gCieXyzClassInfo.X, primaries.blue.X);
+ env->SetFloatField(jblue, gCieXyzClassInfo.Y, primaries.blue.Y);
+ env->SetFloatField(jblue, gCieXyzClassInfo.Z, primaries.blue.Z);
+ env->SetFloatField(jwhite, gCieXyzClassInfo.X, primaries.white.X);
+ env->SetFloatField(jwhite, gCieXyzClassInfo.Y, primaries.white.Y);
+ env->SetFloatField(jwhite, gCieXyzClassInfo.Z, primaries.white.Z);
+ env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.red, jred);
+ env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.green, jgreen);
+ env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.blue, jblue);
+ env->SetObjectField(jprimaries, gDisplayPrimariesClassInfo.white, jwhite);
+
+ return jprimaries;
+}
+
static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return -1;
@@ -1089,6 +1167,8 @@
(void*)nativeSetActiveConfig },
{"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
(void*)nativeGetDisplayColorModes},
+ {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
+ (void*)nativeGetDisplayNativePrimaries },
{"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I",
(void*)nativeGetActiveColorMode},
{"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
@@ -1209,6 +1289,28 @@
displayedContentSamplingAttributesClazz);
gDisplayedContentSamplingAttributesClassInfo.ctor = GetMethodIDOrDie(env,
displayedContentSamplingAttributesClazz, "<init>", "(III)V");
+
+ jclass cieXyzClazz = FindClassOrDie(env, "android/view/SurfaceControl$CieXyz");
+ gCieXyzClassInfo.clazz = MakeGlobalRefOrDie(env, cieXyzClazz);
+ gCieXyzClassInfo.ctor = GetMethodIDOrDie(env, gCieXyzClassInfo.clazz, "<init>", "()V");
+ gCieXyzClassInfo.X = GetFieldIDOrDie(env, cieXyzClazz, "X", "F");
+ gCieXyzClassInfo.Y = GetFieldIDOrDie(env, cieXyzClazz, "Y", "F");
+ gCieXyzClassInfo.Z = GetFieldIDOrDie(env, cieXyzClazz, "Z", "F");
+
+ jclass displayPrimariesClazz = FindClassOrDie(env,
+ "android/view/SurfaceControl$DisplayPrimaries");
+ gDisplayPrimariesClassInfo.clazz = MakeGlobalRefOrDie(env, displayPrimariesClazz);
+ gDisplayPrimariesClassInfo.ctor = GetMethodIDOrDie(env, gDisplayPrimariesClassInfo.clazz,
+ "<init>", "()V");
+ gDisplayPrimariesClassInfo.red = GetFieldIDOrDie(env, displayPrimariesClazz, "red",
+ "Landroid/view/SurfaceControl$CieXyz;");
+ gDisplayPrimariesClassInfo.green = GetFieldIDOrDie(env, displayPrimariesClazz, "green",
+ "Landroid/view/SurfaceControl$CieXyz;");
+ gDisplayPrimariesClassInfo.blue = GetFieldIDOrDie(env, displayPrimariesClazz, "blue",
+ "Landroid/view/SurfaceControl$CieXyz;");
+ gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
+ "Landroid/view/SurfaceControl$CieXyz;");
+
return err;
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 6ee9606..b7837ef 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -276,6 +276,7 @@
// Must match values in com.android.internal.os.Zygote.
enum RuntimeFlags : uint32_t {
DEBUG_ENABLE_JDWP = 1,
+ PROFILE_FROM_SHELL = 1 << 15,
};
// Forward declaration so we don't have to move the signal handler.
@@ -1224,6 +1225,13 @@
if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
EnableDebugger();
}
+ if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) {
+ // simpleperf needs the process to be dumpable to profile it.
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+ ALOGE("prctl(PR_SET_DUMPABLE) failed: %s", strerror(errno));
+ RuntimeAbort(env, __LINE__, "prctl(PR_SET_DUMPABLE, 1) failed");
+ }
+ }
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index 1325b0c..16ef753 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -18,6 +18,8 @@
#define CORE_JNI_HELPERS
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <nativehelper/scoped_utf_chars.h>
#include <android_runtime/AndroidRuntime.h>
namespace android {
@@ -72,6 +74,20 @@
return res;
}
+/**
+ * Read the specified field from jobject, and convert to std::string.
+ * If the field cannot be obtained, return defaultValue.
+ */
+static inline std::string getStringField(JNIEnv* env, jobject obj, jfieldID fieldId,
+ const char* defaultValue) {
+ ScopedLocalRef<jstring> strObj(env, jstring(env->GetObjectField(obj, fieldId)));
+ if (strObj != nullptr) {
+ ScopedUtfChars chars(env, strObj.get());
+ return std::string(chars.c_str());
+ }
+ return std::string(defaultValue);
+}
+
} // namespace android
#endif // CORE_JNI_HELPERS
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index f2f7304..631fa1d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -595,6 +595,11 @@
// ACTION: Tap & Pay -> Default Application Setting -> Use Default
ACTION_NFC_PAYMENT_ALWAYS_SETTING = 1623;
+
+ // ACTION: Settings > Search Bar > Avatar
+ // CATEGORY: SETTINGS
+ // OS: Q
+ CLICK_ACCOUNT_AVATAR = 1643;
}
/**
diff --git a/core/res/res/drawable/chooser_content_preview_rounded.xml b/core/res/res/drawable/chooser_content_preview_rounded.xml
new file mode 100644
index 0000000..7d85738
--- /dev/null
+++ b/core/res/res/drawable/chooser_content_preview_rounded.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+
+ <stroke
+ android:width="1dp"
+ android:color="#ccc" />
+
+ <corners android:radius="@dimen/chooser_corner_radius" />
+
+ <padding
+ android:left="16dp"
+ android:top="12dp"
+ android:right="16dp"
+ android:bottom="12dp"/>
+</shape>
diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml
new file mode 100644
index 0000000..940da94
--- /dev/null
+++ b/core/res/res/drawable/ic_content_copy_gm2.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#F999"
+ android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/>
+</vector>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index c42f43a..a24f920 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -17,19 +17,19 @@
*/
-->
<com.android.internal.widget.ResolverDrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:maxWidth="@dimen/resolver_max_width"
- android:maxCollapsedHeight="288dp"
- android:maxCollapsedHeightSmall="56dp"
- android:id="@id/contentPanel">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:maxWidth="@dimen/resolver_max_width"
+ android:maxCollapsedHeight="288dp"
+ android:maxCollapsedHeightSmall="56dp"
+ android:id="@id/contentPanel">
<RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alwaysShow="true"
- android:background="?attr/colorBackgroundFloating" >
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alwaysShow="true"
+ android:background="?attr/colorBackgroundFloating">
<TextView android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
@@ -51,73 +51,98 @@
android:textAppearance="?attr/textAppearanceMedium"
android:textSize="20sp"
android:gravity="center"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="12dp"
- android:paddingBottom="6dp"
+ android:paddingTop="18dp"
+ android:paddingBottom="18dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
android:layout_below="@id/profile_button"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
- <RelativeLayout
+ <LinearLayout
android:id="@+id/content_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/chooser_view_spacing"
android:background="?attr/colorBackgroundFloating">
-
- <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
- android:id="@+id/content_preview_thumbnail"
- android:layout_alignParentTop="true"
- android:layout_width="100dp"
+
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:gravity="center"
- android:adjustViewBounds="true"
- android:maxWidth="90dp"
- android:maxHeight="90dp"
- android:scaleType="fitCenter"
- android:padding="5dp"/>
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/chooser_edge_margin_normal"
+ android:paddingRight="@dimen/chooser_edge_margin_normal"
+ android:layout_marginBottom="@dimen/chooser_view_spacing"
+ android:id="@+id/content_preview_text_layout">
+ <TextView
+ android:id="@+id/content_preview_text"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:gravity="start|top"
+ android:paddingRight="24dp"
+ android:maxLines="2"/>
+ <Button
+ android:id="@+id/copy_button"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:gravity="center"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/ic_content_copy_gm2"/>
+ </LinearLayout>
- <TextView
- android:id="@+id/content_preview_title"
- android:layout_alignParentTop="true"
- android:layout_toEndOf="@id/content_preview_thumbnail"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="start|top"
- android:textAppearance="?attr/textAppearanceMedium"
- android:maxLines="2"
- android:ellipsize="end"
- android:paddingStart="15dp"
- android:paddingEnd="15dp"
- android:paddingTop="10dp" />
+ <!-- Required sub-layout so we can get the nice rounded corners-->
+ <!-- around this section -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginLeft="@dimen/chooser_edge_margin_thin"
+ android:layout_marginRight="@dimen/chooser_edge_margin_thin"
+ android:minHeight="80dp"
+ android:background="@drawable/chooser_content_preview_rounded"
+ android:id="@+id/content_preview_title_layout">
- <TextView
- android:id="@+id/content_preview_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/content_preview_title"
- android:layout_toEndOf="@id/content_preview_thumbnail"
- android:gravity="start|top"
- android:maxLines="2"
- android:ellipsize="end"
- android:paddingStart="15dp"
- android:paddingEnd="15dp"
- android:paddingTop="10dp"
- android:paddingBottom="5dp"/>
- </RelativeLayout>
+ <view class="com.android.internal.app.ChooserActivity$RoundedRectImageView"
+ android:id="@+id/content_preview_thumbnail"
+ android:layout_width="80dp"
+ android:layout_height="80dp"
+ android:layout_marginRight="12dp"
+ android:adjustViewBounds="true"
+ android:layout_gravity="center_vertical"
+ android:gravity="center"
+ android:maxWidth="70dp"
+ android:maxHeight="70dp"
+ android:padding="5dp"
+ android:scaleType="centerCrop"/>
+
+ <TextView
+ android:id="@+id/content_preview_title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:textAppearance="?attr/textAppearanceMedium"/>
+ </LinearLayout>
+ </LinearLayout>
<ListView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/resolver_list"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
- android:listSelector="@color/transparent"
- android:divider="@null"
- android:scrollIndicators="top"
- android:nestedScrollingEnabled="true" />
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/resolver_list"
+ android:clipToPadding="false"
+ android:scrollbarStyle="outsideOverlay"
+ android:background="?attr/colorBackgroundFloating"
+ android:elevation="8dp"
+ android:listSelector="@color/transparent"
+ android:divider="@null"
+ android:scrollIndicators="top"
+ android:nestedScrollingEnabled="true"/>
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
@@ -127,6 +152,6 @@
android:text="@string/noApplications"
android:padding="32dp"
android:gravity="center"
- android:visibility="gone" />
+ android:visibility="gone"/>
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1eece03..7219d5c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1138,10 +1138,12 @@
resource] to be present in order to function. Default value is false. -->
<attr name="isSplitRequired" format="boolean" />
- <!-- Flag to specify if this app prioritizes code integrity. The system may choose
- to run with better integrity guarantee in various components if possible based on the app's
- <code>targetSdkVersion</code>. -->
- <attr name="preferCodeIntegrity" format="boolean" />
+ <!-- Flag to specify if this app wants to run the dex within its APK but not extracted or
+ locally compiled variants. This keeps the dex code protected by the APK signature. Such
+ apps will always run in JIT mode (same when they are first installed), and the system will
+ never generate ahead-of-time compiled code for them. Depending on the app's workload,
+ there may be some run time performance change, noteably the cold start time. -->
+ <attr name="useEmbeddedDex" format="boolean" />
<!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
{@code <application>} tag. If specified on the {@code <application>}
@@ -1445,26 +1447,20 @@
<attr name="usesNonSdkApi" format="boolean" />
- <!-- Specify the type of foreground service. Apps targeting API
- {@link android.os.Build.VERSION_CODES#Q} or later must specify foreground service type,
- otherwise a SecurityException is thrown when
- {@link android.app.Service#startForeground(int, Notification)} on this service is called.
- -->
+ <!-- Specify the type of foreground service. Multiple types can be specified by ORing the flags
+ together. -->
<attr name="foregroundServiceType">
<!-- Data (photo, file, account) upload/download, backup/restore, import/export, fetch,
transfer over network between device and cloud. -->
- <enum name="sync" value="1" />
+ <flag name="dataSync" value="0x01" />
<!-- Music, video, news or other media play. -->
- <enum name="mediaPlay" value="2" />
+ <flag name="mediaPlayback" value="0x02" />
<!-- Ongoing phone call or video conference. -->
- <enum name="phoneCall" value="3" />
+ <flag name="phoneCall" value="0x04" />
<!-- GPS, map, navigation location update. -->
- <enum name="location" value="4" />
+ <flag name="location" value="0x08" />
<!-- Auto, bluetooth, TV or other devices connection, monitoring and interaction. -->
- <enum name="deviceCompanion" value="5" />
- <!-- Process that should not be interrupted, including installation, setup, photo
- compression etc. -->
- <enum name="ongoingProcess" value="6" />
+ <flag name="connectedDevice" value="0x10" />
</attr>
@@ -1611,7 +1607,7 @@
to honor this flag as well. -->
<attr name="usesCleartextTraffic" />
<attr name="multiArch" />
- <attr name="preferCodeIntegrity" />
+ <attr name="useEmbeddedDex" />
<attr name="extractNativeLibs" />
<attr name="defaultToDeviceProtectedStorage" format="boolean" />
<attr name="directBootAware" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index eb214ba..df20f85 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1025,7 +1025,8 @@
<integer name="config_displayWhiteBalanceColorTemperatureDefault">6500</integer>
<!-- The display primaries, in CIE1931 XYZ color space, for display
- white balance to use in its calculations. -->
+ white balance to use in its calculations. The array must include a total of 12 float
+ values: 3 values per color (X, Y, Z) and 4 colors (R, G, B, W) -->
<string-array name="config_displayWhiteBalanceDisplayPrimaries">
<!-- Red X --> <item>0.412315</item>
<!-- Red Y --> <item>0.212600</item>
@@ -1043,7 +1044,7 @@
<!-- The nominal white coordinates, in CIE1931 XYZ color space, for Display White Balance to
use in its calculations. AWB will adapt this white point to the target ambient white
- point. -->
+ point. The array must include a total of 3 float values (X, Y, Z) -->
<string-array name="config_displayWhiteBalanceDisplayNominalWhite">
<!-- Nominal White X --> <item>0.950456</item>
<!-- Nominal White Y --> <item>1.000000</item>
@@ -3781,4 +3782,105 @@
<!-- Whether or not aware is enabled by default -->
<bool name="config_awareSettingAvailable">false</bool>
+
+ <!-- Display White-Balance -->
+
+ <!-- See AmbientSensor.AmbientBrightnessSensor.
+ The ambient brightness sensor rate (in milliseconds). Must be positive. -->
+ <integer name="config_displayWhiteBalanceBrightnessSensorRate">250</integer>
+
+ <!-- See AmbientFilter.
+ How long ambient brightness changes are kept and taken into consideration
+ (in milliseconds). Must be positive. -->
+ <integer name="config_displayWhiteBalanceBrightnessFilterHorizon">10000</integer>
+
+ <!-- See AmbientFilter.WeightedMovingAverageAmbientFilter.
+ Recent changes are prioritised by integrating their duration over y = x + intercept
+ (the higher it is, the less prioritised recent changes are). Must be a non-negative
+ number, or NaN to avoid this implementation. -->
+ <item name="config_displayWhiteBalanceBrightnessFilterIntercept" format="float" type="dimen">10.0</item>
+
+ <!-- See AmbientSensor.AmbientColorTemperatureSensor.
+ The ambient color temperature sensor name. -->
+ <string name="config_displayWhiteBalanceColorTemperatureSensorName">com.google.sensor.color</string>
+
+ <!-- See AmbientSensor.AmbientColorTemperatureSensor.
+ The ambient color temperature sensor rate (in milliseconds). Must be positive. -->
+ <integer name="config_displayWhiteBalanceColorTemperatureSensorRate">250</integer>
+
+ <!-- See AmbientFilter.
+ How long ambient color temperature changes are kept and taken into consideration
+ (in milliseconds). Must be positive. -->
+ <integer name="config_displayWhiteBalanceColorTemperatureFilterHorizon">10000</integer>
+
+ <!-- See AmbientFilter.WeightedMovingAverageAmbientFilter.
+ Recent changes are prioritised by integrating their duration over y = x + intercept
+ (the higher it is, the less prioritised recent changes are). Must be a non-negative
+ number, or NaN to avoid this implementation. -->
+ <item name="config_displayWhiteBalanceColorTemperatureFilterIntercept" format="float"
+ type="dimen">10.0</item>
+
+ <!-- See DisplayWhiteBalanceThrottler.
+ The debounce time (in milliseconds) for increasing the screen color temperature, throttled
+ if time > lastTime + debounce. Must be non-negative. -->
+ <integer name="config_displayWhiteBalanceIncreaseDebounce">5000</integer>
+
+ <!-- See DisplayWhiteBalanceThrottler.
+ The debounce time (in milliseconds) for decreasing the screen color tempearture, throttled
+ if time < lastTime - debounce. Must be non-negative. -->
+ <integer name="config_displayWhiteBalanceDecreaseDebounce">5000</integer>
+
+ <!-- See DisplayWhiteBalanceThrottler.
+ The ambient color temperature values used to determine the threshold as the corresponding
+ value in config_displayWhiteBalance{Increase,Decrease}Threholds. Must be non-empty, the
+ same length as config_displayWhiteBalance{Increase,Decrease}Thresholds, and contain
+ non-negative, strictly increasing numbers.
+
+ For example, if:
+
+ - baseThresolds = [0, 100, 1000];
+ - increaseThresholds = [0.1, 0.15, 0.2];
+ - decreaseThresholds = [0.1, 0.05, 0.0];
+
+ Then, given the ambient color temperature INCREASED from X to Y (so X < Y):
+ - If 0 <= Y < 100, we require Y > (1 + 0.1) * X = 1.1X;
+ - If 100 <= Y < 1000, we require Y > (1 + 0.15) * X = 1.15X;
+ - If 1000 <= Y, we require Y > (1 + 0.2) * X = 1.2X.
+
+ Or, if the ambient color temperature DECREASED from X to Y (so X > Y):
+ - If 0 <= Y < 100, we require Y < (1 - 0.1) * X = 0.9X;
+ - If 100 <= Y < 1000, we require Y < (1 - 0.05) * X = 0.95X;
+ - If 1000 <= Y, we require Y < (1 - 0) * X = X.
+
+ NOTE: the numbers in this example are made up, and don't represent how actual base,
+ increase or decrease thresholds would look like. -->
+ <array name="config_displayWhiteBalanceBaseThresholds">
+ <item>0.0</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceThrottler.
+ The increase threshold values, throttled if value < value * (1 + threshold). Must be
+ non-empty, the same length as config_displayWhiteBalanceBaseThresholds, and contain
+ non-negative numbers. -->
+ <array name="config_displayWhiteBalanceIncreaseThresholds">
+ <item>0.1</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceThrottler.
+ The decrease threshold values, throttled if value > value * (1 - threshold). Must be
+ non-empty, the same length as config_displayWhiteBalanceBaseThresholds, and contain
+ non-negative numbers. -->
+ <array name="config_displayWhiteBalanceDecreaseThresholds">
+ <item>0.1</item>
+ </array>
+
+ <!-- See DisplayWhiteBalanceController.
+ The ambient brightness threshold (in lux) beneath which we fall back to a fixed ambient
+ color temperature. -->
+ <item name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" format="float" type="dimen">10.0</item>
+
+ <!-- See DisplayWhiteBalanceController.
+ The ambient color temperature (in cct) to which we fall back when the ambient brightness
+ drops beneath a certain threshold. -->
+ <item name="config_displayWhiteBalanceLowLightAmbientColorTemperature" format="float" type="dimen">6500.0</item>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index baf7587..38367fb 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -715,6 +715,9 @@
<!-- Line spacing modifier for the message field of the harmful app dialog -->
<item name="harmful_app_message_line_spacing_modifier" type="dimen">1.22</item>
- <!-- chooser corner radius -->
- <dimen name="chooser_corner_radius">4dp</dimen>
+ <!-- chooser (sharesheet) spacing -->
+ <dimen name="chooser_corner_radius">8dp</dimen>
+ <dimen name="chooser_view_spacing">18dp</dimen>
+ <dimen name="chooser_edge_margin_thin">16dp</dimen>
+ <dimen name="chooser_edge_margin_normal">24dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b5266e2..e7d8102 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2937,6 +2937,7 @@
<!-- @hide @SystemApi -->
<public name="inheritShowWhenLocked" />
<public name="zygotePreloadName" />
+ <public name="useEmbeddedDex" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 04df97c..fadb28f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2645,6 +2645,9 @@
<!-- Displayed to the user to confirm that they have copied text from a web page to the clipboard. -->
<string name="text_copied">Text copied to clipboard.</string>
+ <!-- Displayed to the user to confirm that they have copied text/images to the clipboard [CHAR LIMIT=NONE] -->
+ <string name="copied">Copied</string>
+
<!-- Menu item displayed at the end of a menu to allow users to see another page worth of menu items. This is shown on any app's menu as long as the app has too many items in the menu.-->
<string name="more_item_label">More</string>
<!-- Prepended to the shortcut for a menu item to indicate that the user should hold the MENU button together with the shortcut to invoke the item. For example, if the shortcut to open a new tab in browser is MENU and B together, then this would be prepended to the letter "B" -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 52400de..ca8fa82 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -53,7 +53,10 @@
<java-symbol type="id" name="content_preview" />
<java-symbol type="id" name="content_preview_thumbnail" />
<java-symbol type="id" name="content_preview_text" />
+ <java-symbol type="id" name="content_preview_text_layout" />
<java-symbol type="id" name="content_preview_title" />
+ <java-symbol type="id" name="content_preview_title_layout" />
+ <java-symbol type="id" name="copy_button" />
<java-symbol type="id" name="current_scene" />
<java-symbol type="id" name="scene_layoutid_cache" />
<java-symbol type="id" name="customPanel" />
@@ -545,6 +548,7 @@
<java-symbol type="string" name="activity_resolver_work_profiles_support" />
<java-symbol type="string" name="app_running_notification_title" />
<java-symbol type="string" name="app_running_notification_text" />
+ <java-symbol type="string" name="copied" />
<java-symbol type="string" name="delete" />
<java-symbol type="string" name="deleteText" />
<java-symbol type="string" name="grant_permissions_header_text" />
@@ -2711,6 +2715,9 @@
<java-symbol type="id" name="month_view" />
<java-symbol type="integer" name="config_zen_repeat_callers_threshold" />
<java-symbol type="dimen" name="chooser_corner_radius" />
+ <java-symbol type="dimen" name="chooser_view_spacing" />
+ <java-symbol type="dimen" name="chooser_edge_margin_thin" />
+ <java-symbol type="dimen" name="chooser_edge_margin_normal" />
<java-symbol type="layout" name="chooser_grid" />
<java-symbol type="layout" name="resolve_grid_item" />
<java-symbol type="id" name="day_picker_view_pager" />
@@ -3591,4 +3598,20 @@
<java-symbol type="integer" name="config_attentionApiTimeout" />
<java-symbol type="string" name="config_incidentReportApproverPackage" />
+
+ <!-- Display White-Balance -->
+ <java-symbol type="integer" name="config_displayWhiteBalanceBrightnessSensorRate" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceBrightnessFilterHorizon" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceBrightnessFilterIntercept" />
+ <java-symbol type="string" name="config_displayWhiteBalanceColorTemperatureSensorName" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureSensorRate" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureFilterHorizon" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceColorTemperatureFilterIntercept" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceIncreaseDebounce" />
+ <java-symbol type="integer" name="config_displayWhiteBalanceDecreaseDebounce" />
+ <java-symbol type="array" name="config_displayWhiteBalanceBaseThresholds" />
+ <java-symbol type="array" name="config_displayWhiteBalanceIncreaseThresholds" />
+ <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
+ <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
</resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 268bb81..1764249 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -59,6 +59,7 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
<uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
+ <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS"/>
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 21fcae7..6b9e69c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -34,6 +34,9 @@
import android.app.usage.UsageStatsManager;
import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ClipboardManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
@@ -392,6 +395,32 @@
assertThat(chosen[0], is(toChoose));
}
+ @Test
+ public void copyTextToClipboard() throws Exception {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos =
+ createResolvedComponentsForTestWithOtherProfile(1);
+
+ when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.copy_button)).perform(click());
+
+ ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ ClipData clipData = clipboard.getPrimaryClip();
+ assertThat("testing intent sending", is(clipData.getItemAt(0).getText()));
+
+ ClipDescription clipDescription = clipData.getDescription();
+ assertThat("text/plain", is(clipDescription.getMimeType(0)));
+ }
+
private Intent createSendTextIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 39bfcdd..6b7ec97 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -86,7 +86,7 @@
return sEglManager.eglDisplay();
}
-static bool hasFP16Support() {
+bool HardwareBitmapUploader::hasFP16Support() {
static std::once_flag sOnce;
static bool hasFP16Support = false;
@@ -127,7 +127,7 @@
formatInfo.type = GL_UNSIGNED_BYTE;
break;
case kRGBA_F16_SkColorType:
- formatInfo.isSupported = hasFP16Support();
+ formatInfo.isSupported = HardwareBitmapUploader::hasFP16Support();
if (formatInfo.isSupported) {
formatInfo.type = GL_HALF_FLOAT;
formatInfo.pixelFormat = PIXEL_FORMAT_RGBA_FP16;
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 40f2b0c..6f41e6d 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -20,10 +20,12 @@
namespace android::uirenderer {
-class HardwareBitmapUploader {
+class ANDROID_API HardwareBitmapUploader {
public:
static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
static void terminate();
+
+ static bool hasFP16Support();
};
} // namespace android::uirenderer
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 2d2c4a8..9258b85 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1186,22 +1186,22 @@
public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0;
/**
- * Offline license state is usable, the keys may be used for decryption.
+ * Offline license is usable, the keys may be used for decryption.
*/
- public static final int OFFLINE_LICENSE_USABLE = 1;
+ public static final int OFFLINE_LICENSE_STATE_USABLE = 1;
/**
- * Offline license state is inactive, the keys have been marked for
- * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but the
- * key response has not been received.
+ * Offline license is released, the keys have been marked for
+ * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but
+ * the key response has not been received.
*/
- public static final int OFFLINE_LICENSE_INACTIVE = 2;
+ public static final int OFFLINE_LICENSE_STATE_RELEASED = 2;
/** @hide */
@IntDef({
OFFLINE_LICENSE_STATE_UNKNOWN,
- OFFLINE_LICENSE_USABLE,
- OFFLINE_LICENSE_INACTIVE,
+ OFFLINE_LICENSE_STATE_USABLE,
+ OFFLINE_LICENSE_STATE_RELEASED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OfflineLicenseState {}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 866325c..d7ab854 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -175,7 +175,7 @@
struct OfflineLicenseState {
jint kOfflineLicenseStateUsable;
- jint kOfflineLicenseStateInactive;
+ jint kOfflineLicenseStateReleased;
jint kOfflineLicenseStateUnknown;
} gOfflineLicenseStates;
@@ -797,10 +797,10 @@
GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I");
gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_USABLE", "I");
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_USABLE", "I");
gOfflineLicenseStates.kOfflineLicenseStateUsable = env->GetStaticIntField(clazz, field);
- GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_INACTIVE", "I");
- gOfflineLicenseStates.kOfflineLicenseStateInactive = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_RELEASED", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateReleased = env->GetStaticIntField(clazz, field);
GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_UNKNOWN", "I");
gOfflineLicenseStates.kOfflineLicenseStateUnknown = env->GetStaticIntField(clazz, field);
@@ -1581,8 +1581,8 @@
switch(state) {
case DrmPlugin::kOfflineLicenseStateUsable:
return gOfflineLicenseStates.kOfflineLicenseStateUsable;
- case DrmPlugin::kOfflineLicenseStateInactive:
- return gOfflineLicenseStates.kOfflineLicenseStateInactive;
+ case DrmPlugin::kOfflineLicenseStateReleased:
+ return gOfflineLicenseStates.kOfflineLicenseStateReleased;
default:
return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
}
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index f3442f4..c6b171b 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -913,13 +913,9 @@
}
// build and return the Bundle
- MediaAnalyticsItem *item = new MediaAnalyticsItem;
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
item->readFromParcel(reply);
- jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
-
- // housekeeping
- delete item;
- item = NULL;
+ jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
return mybundle;
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 76bbce7..35b1081 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -682,13 +682,9 @@
return (jobject) NULL;
}
- MediaAnalyticsItem *item = new MediaAnalyticsItem;
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
item->readFromParcel(p);
- jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
-
- // housekeeping
- delete item;
- item = NULL;
+ jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
return mybundle;
}
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index ca30f32..a7c0159 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -664,15 +664,11 @@
}
// build and return the Bundle
- MediaAnalyticsItem *item = new MediaAnalyticsItem;
+ std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create());
item->readFromParcel(reply);
- jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
+ jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item.get(), NULL);
- // housekeeping
- delete item;
- item = NULL;
return mybundle;
-
}
static jboolean
diff --git a/media/native/midi/include/Doxyfile b/media/native/midi/include/Doxyfile
new file mode 100644
index 0000000..2828f48
--- /dev/null
+++ b/media/native/midi/include/Doxyfile
@@ -0,0 +1,2494 @@
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Android Native MIDI API (AMidi)"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =" Android Native MIDI API (AMidi)"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = "midi.h"
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/media/native/midi/include/Doxyfile.orig b/media/native/midi/include/Doxyfile.orig
new file mode 100644
index 0000000..e56fb18
--- /dev/null
+++ b/media/native/midi/include/Doxyfile.orig
@@ -0,0 +1,2494 @@
+# Doxyfile 1.8.13
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "My Project"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS = *.c \
+ *.cc \
+ *.cxx \
+ *.cpp \
+ *.c++ \
+ *.java \
+ *.ii \
+ *.ixx \
+ *.ipp \
+ *.i++ \
+ *.inl \
+ *.idl \
+ *.ddl \
+ *.odl \
+ *.h \
+ *.hh \
+ *.hxx \
+ *.hpp \
+ *.h++ \
+ *.cs \
+ *.d \
+ *.php \
+ *.php4 \
+ *.php5 \
+ *.phtml \
+ *.inc \
+ *.m \
+ *.markdown \
+ *.md \
+ *.mm \
+ *.dox \
+ *.py \
+ *.pyw \
+ *.f90 \
+ *.f95 \
+ *.f03 \
+ *.f08 \
+ *.f \
+ *.for \
+ *.tcl \
+ *.vhd \
+ *.vhdl \
+ *.ucf \
+ *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new.xml b/packages/SystemUI/res/drawable/ic_open_in_new.xml
new file mode 100644
index 0000000..3d53871
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_open_in_new.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2019 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index 1aeb52c..f0d2b2e 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -18,6 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
+ android:orientation="vertical"
android:id="@+id/bubble_expanded_view">
<View
@@ -26,17 +27,48 @@
android:layout_height="@dimen/bubble_pointer_height"
/>
- <TextView
- android:id="@+id/bubble_content_header"
- android:background="@drawable/bubble_expanded_header_bg"
- android:textAppearance="@*android:style/TextAppearance.Material.Title"
- android:textSize="18sp"
- android:layout_width="match_parent"
+ <LinearLayout
+ android:id="@+id/header_layout"
android:layout_height="@dimen/bubble_expanded_header_height"
- android:gravity="start|center_vertical"
- android:singleLine="true"
- android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
- android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
- />
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:background="@drawable/bubble_expanded_header_bg">
+
+ <TextView
+ android:id="@+id/header_text"
+ android:textAppearance="@*android:style/TextAppearance.Material.Title"
+ android:textSize="18sp"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical"
+ android:singleLine="true"
+ android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
+ android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
+ />
+
+ <ImageButton
+ android:id="@+id/deep_link_button"
+ android:layout_width="@dimen/bubble_header_icon_size"
+ android:layout_height="@dimen/bubble_header_icon_size"
+ android:gravity="end|center_vertical"
+ android:src="@drawable/ic_open_in_new"
+ android:scaleType="center"
+ android:tint="?android:attr/colorForeground"
+ android:background="?android:attr/selectableItemBackground"
+ />
+
+ <ImageButton
+ android:id="@id/settings_button"
+ android:layout_width="@dimen/bubble_header_icon_size"
+ android:layout_height="@dimen/bubble_header_icon_size"
+ android:src="@drawable/ic_settings"
+ android:gravity="end|center_vertical"
+ android:scaleType="center"
+ android:tint="?android:attr/colorForeground"
+ android:background="?android:attr/selectableItemBackground"
+ />
+
+ </LinearLayout>
</com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d435c67..8f5effb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1030,4 +1030,6 @@
<dimen name="bubble_stack_offscreen">5dp</dimen>
<!-- How far down the screen the stack starts. -->
<dimen name="bubble_stack_starting_offset_y">100dp</dimen>
+ <!-- Size of image buttons in the bubble header -->
+ <dimen name="bubble_header_icon_size">48dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ffa5de8..56d9bf4c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2309,7 +2309,7 @@
<string name="ongoing_privacy_dialog_ok">Got it</string>
<!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=20]-->
- <string name="ongoing_privacy_dialog_open_settings">View details</string>
+ <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
<!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
<string name="ongoing_privacy_dialog_single_app_title">App using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
@@ -2346,4 +2346,12 @@
<!-- What to show on the ambient display player when song doesn't have a title. [CHAR LIMIT=20] -->
<string name="music_controls_no_title">No title</string>
+
+ <!-- Text used for content description of deep link button in the header of expanded bubble
+ view. [CHAR_LIMIT=NONE] -->
+ <string name="bubbles_deep_link_button_description">Open <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+ <!-- Text used for content description of settings button in the header of expanded bubble
+ view. [CHAR_LIMIT=NONE] -->
+ <string name="bubbles_settings_button_description">Open notification settings for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bcd41a0..0970c21 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -99,6 +99,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.TimeZone;
+import java.util.function.Consumer;
/**
* Watches for updates that may be interesting to the keyguard, and provides
@@ -218,7 +219,8 @@
// Battery status
private BatteryStatus mBatteryStatus;
- private final StrongAuthTracker mStrongAuthTracker;
+ @VisibleForTesting
+ protected StrongAuthTracker mStrongAuthTracker;
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();
@@ -344,8 +346,7 @@
handleUserUnlocked();
break;
case MSG_ASSISTANT_STACK_CHANGED:
- mAssistantVisible = (boolean)msg.obj;
- updateBiometricListeningState();
+ setAssistantVisible((boolean) msg.obj);
break;
case MSG_BIOMETRIC_AUTHENTICATION_CONTINUE:
updateBiometricListeningState();
@@ -354,7 +355,7 @@
updateLogoutEnabled();
break;
case MSG_TELEPHONY_CAPABLE:
- updateTelephonyCapable((boolean)msg.obj);
+ updateTelephonyCapable((boolean) msg.obj);
break;
default:
super.handleMessage(msg);
@@ -538,7 +539,8 @@
}
}
- private void onFingerprintAuthenticated(int userId) {
+ @VisibleForTesting
+ protected void onFingerprintAuthenticated(int userId) {
Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
mUserFingerprintAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
@@ -693,7 +695,8 @@
}
}
- private void onFaceAuthenticated(int userId) {
+ @VisibleForTesting
+ protected void onFaceAuthenticated(int userId) {
Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
mUserFaceAuthenticated.put(userId, true);
// Update/refresh trust state only if user can skip bouncer
@@ -898,8 +901,9 @@
public boolean getUserCanSkipBouncer(int userId) {
- return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
- && isUnlockingWithBiometricAllowed());
+ boolean fingerprintOrFace = mUserFingerprintAuthenticated.get(userId)
+ || mUserFaceAuthenticated.get(userId);
+ return getUserHasTrust(userId) || (fingerprintOrFace && isUnlockingWithBiometricAllowed());
}
public boolean getUserHasTrust(int userId) {
@@ -950,6 +954,12 @@
}
}
+ @VisibleForTesting
+ void setAssistantVisible(boolean assistantVisible) {
+ mAssistantVisible = assistantVisible;
+ updateBiometricListeningState();
+ }
+
static class DisplayClientState {
public int clientGeneration;
public boolean clearing;
@@ -1047,7 +1057,8 @@
}
};
- private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
+ @VisibleForTesting
+ protected final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1121,7 +1132,8 @@
}
};
- private FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+ @VisibleForTesting
+ FaceManager.AuthenticationCallback mFaceAuthenticationCallback
= new FaceManager.AuthenticationCallback() {
@Override
@@ -1298,9 +1310,13 @@
}
}
- public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
- public StrongAuthTracker(Context context) {
+ public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+ private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
+
+ public StrongAuthTracker(Context context,
+ Consumer<Integer> strongAuthRequiredChangedCallback) {
super(context);
+ mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
}
public boolean isUnlockingWithBiometricAllowed() {
@@ -1316,7 +1332,7 @@
@Override
public void onStrongAuthRequiredChanged(int userId) {
- notifyStrongAuthStateChanged(userId);
+ mStrongAuthRequiredChangedCallback.accept(userId);
}
}
@@ -1423,7 +1439,7 @@
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
- mStrongAuthTracker = new StrongAuthTracker(context);
+ mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
@@ -1548,6 +1564,14 @@
}
}
+ /**
+ * Request passive authentication, when sensors detect that a user might be present.
+ */
+ public void onAuthInterruptDetected() {
+ if (DEBUG) Log.d(TAG, "onAuthInterruptDetected()");
+ updateFaceListeningState();
+ }
+
private void updateFaceListeningState() {
// If this message exists, we should not authenticate again until this message is
// consumed by the handler
@@ -1585,11 +1609,13 @@
}
private boolean shouldListenForFace() {
- return (mKeyguardIsVisible || !mDeviceInteractive ||
- (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
- shouldListenForFaceAssistant() || (mKeyguardOccluded && mIsDreaming))
- && !mSwitchingUser && !isFaceDisabled(getCurrentUser())
- && !mKeyguardGoingAway && !mFaceLockedOut && mFaceSettingEnabledForUser;
+ final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep;
+ final int user = getCurrentUser();
+
+ return (mBouncer || awakeKeyguard || shouldListenForFaceAssistant())
+ && !mSwitchingUser && !getUserCanSkipBouncer(user) && !isFaceDisabled(user)
+ && !mKeyguardGoingAway && !mFaceLockedOut && mFaceSettingEnabledForUser
+ && mUserManager.isUserUnlocked(user);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e3f6add..0fc2b27 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,6 +42,8 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
+import androidx.annotation.MainThread;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
@@ -289,7 +291,10 @@
/**
* Removes the bubble associated with the {@param uri}.
+ * <p>
+ * Must be called from the main thread.
*/
+ @MainThread
void removeBubble(String key) {
BubbleView bv = mBubbles.remove(key);
if (mStackView != null && bv != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
index 231e725..67b18fd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -17,32 +17,53 @@
package com.android.systemui.bubbles;
import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Container for the expanded bubble view, handles rendering the caret and header of the view.
*/
-public class BubbleExpandedViewContainer extends LinearLayout {
+public class BubbleExpandedViewContainer extends LinearLayout implements View.OnClickListener {
+ private static final String TAG = "BubbleExpandedView";
// The triangle pointing to the expanded view
private View mPointerView;
// The view displayed between the pointer and the expanded view
private TextView mHeaderView;
+ // Tappable header icon deeplinking into the app
+ private ImageButton mDeepLinkIcon;
+ // Tappable header icon deeplinking into notification settings
+ private ImageButton mSettingsIcon;
// The view that is being displayed for the expanded state
private View mExpandedView;
+ private NotificationEntry mEntry;
+ private PackageManager mPm;
+ private String mAppName;
+
+ // Need reference to let it know to collapse when new task is launched
+ private BubbleStackView mStackView;
+
public BubbleExpandedViewContainer(Context context) {
this(context, null);
}
@@ -58,7 +79,7 @@
public BubbleExpandedViewContainer(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- setOrientation(VERTICAL);
+ mPm = context.getPackageManager();
}
@Override
@@ -79,7 +100,75 @@
TriangleShape.create(width, height, true /* pointUp */));
triangleDrawable.setTint(bgColor);
mPointerView.setBackground(triangleDrawable);
- mHeaderView = findViewById(R.id.bubble_content_header);
+
+ mHeaderView = findViewById(R.id.header_text);
+ mDeepLinkIcon = findViewById(R.id.deep_link_button);
+ mSettingsIcon = findViewById(R.id.settings_button);
+ mDeepLinkIcon.setOnClickListener(this);
+ mSettingsIcon.setOnClickListener(this);
+ }
+
+ /**
+ * Sets the notification entry used to populate this view.
+ */
+ public void setEntry(NotificationEntry entry, BubbleStackView stackView) {
+ mStackView = stackView;
+ mEntry = entry;
+
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfo(
+ entry.notification.getPackageName(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ mAppName = String.valueOf(mPm.getApplicationLabel(info));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ahh... just use package name
+ mAppName = entry.notification.getPackageName();
+ }
+
+ updateHeaderView();
+ }
+
+ private void updateHeaderView() {
+ mSettingsIcon.setContentDescription(getResources().getString(
+ R.string.bubbles_settings_button_description, mAppName));
+ mDeepLinkIcon.setContentDescription(getResources().getString(
+ R.string.bubbles_deep_link_button_description, mAppName));
+ if (mEntry != null && mEntry.getBubbleMetadata() != null) {
+ setHeaderText(mEntry.getBubbleMetadata().getTitle());
+ } else {
+ // This should only happen if we're auto-bubbling notification content that isn't
+ // explicitly a bubble
+ setHeaderText(mAppName);
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (mEntry == null) {
+ return;
+ }
+ Notification n = mEntry.notification.getNotification();
+ int id = view.getId();
+ if (id == R.id.deep_link_button) {
+ mStackView.collapseStack(() -> {
+ try {
+ n.contentIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Failed to send intent for bubble with key: "
+ + (mEntry != null ? mEntry.key : " null entry"));
+ }
+ });
+ } else if (id == R.id.settings_button) {
+ Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
+ mEntry.notification.getUid());
+ mStackView.collapseStack(() -> mContext.startActivity(intent));
+ }
}
/**
@@ -94,7 +183,7 @@
/**
* Set the text displayed within the header.
*/
- public void setHeaderText(CharSequence text) {
+ private void setHeaderText(CharSequence text) {
mHeaderView.setText(text);
mHeaderView.setVisibility(TextUtils.isEmpty(text) ? GONE : VISIBLE);
}
@@ -122,4 +211,13 @@
public View getExpandedView() {
return mExpandedView;
}
+
+ private Intent getSettingsIntent(String packageName, final int appUid) {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+ intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index de322e6..b6acd63 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -20,7 +20,6 @@
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.ActivityView;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
@@ -28,7 +27,9 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.util.StatsLog;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -55,6 +56,9 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
/**
* Renders bubbles in a stack and handles animating expanded and collapsed states.
*/
@@ -241,16 +245,20 @@
// Previously expanded, notify that this bubble is no longer expanded
notifyExpansionChanged(mExpandedBubble, false /* expanded */);
}
+ BubbleView prevBubble = mExpandedBubble;
mExpandedBubble = bubbleToExpand;
if (!mIsExpanded) {
// If we weren't previously expanded we should animate open.
animateExpansion(true /* expand */);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
} else {
// Otherwise just update the views
// TODO: probably animate / page to expanded one
updateExpandedBubble();
updatePointerPosition();
requestUpdate();
+ logBubbleEvent(prevBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
}
mExpandedBubble.getEntry().setShowInShadeWhenBubble(false);
notifyExpansionChanged(mExpandedBubble, true /* expanded */);
@@ -279,6 +287,7 @@
new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
ViewClippingUtil.setClippingDeactivated(bubbleView, true, mClippingParameters);
requestUpdate();
+ logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
}
/**
@@ -287,20 +296,24 @@
public void removeBubble(BubbleView bubbleView) {
int removedIndex = mBubbleContainer.indexOfChild(bubbleView);
mBubbleContainer.removeView(bubbleView);
- boolean wasExpanded = mIsExpanded;
int bubbleCount = mBubbleContainer.getChildCount();
- if (mIsExpanded && bubbleView.equals(mExpandedBubble) && bubbleCount > 0) {
+ if (bubbleCount == 0) {
+ // If no bubbles remain, collapse the entire stack.
+ collapseStack();
+ return;
+ } else if (bubbleView.equals(mExpandedBubble)) {
+ // Was the current bubble just removed?
// If we have other bubbles and are expanded go to the next one or previous
// if the bubble removed was last
int nextIndex = bubbleCount > removedIndex ? removedIndex : bubbleCount - 1;
BubbleView expandedBubble = (BubbleView) mBubbleContainer.getChildAt(nextIndex);
- setExpandedBubble(expandedBubble);
- requestUpdate();
+ if (mIsExpanded) {
+ setExpandedBubble(expandedBubble);
+ } else {
+ mExpandedBubble = null;
+ }
}
- mIsExpanded = wasExpanded && mBubbleContainer.getChildCount() > 0;
- if (wasExpanded != mIsExpanded) {
- notifyExpansionChanged(mExpandedBubble, mIsExpanded);
- }
+ logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
}
/**
@@ -309,6 +322,8 @@
public void stackDismissed() {
collapseStack();
mBubbleContainer.removeAllViews();
+ logBubbleEvent(null /* no bubble associated with bubble stack dismiss */,
+ StatsLog.BUBBLE_UICHANGED__ACTION__STACK_DISMISSED);
}
/**
@@ -332,6 +347,7 @@
entry.setShowInShadeWhenBubble(false);
requestUpdate();
}
+ logBubbleEvent(bubbleView, StatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
}
/**
@@ -367,9 +383,16 @@
// TODO: Save opened bubble & move it to top of stack
animateExpansion(false /* shouldExpand */);
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
}
}
+ void collapseStack(Runnable endRunnable) {
+ collapseStack();
+ // TODO - use the runnable at end of animation
+ endRunnable.run();
+ }
+
/**
* Expands the stack fo bubbles.
*/
@@ -377,6 +400,7 @@
if (!mIsExpanded) {
mExpandedBubble = getTopBubble();
setExpandedBubble(mExpandedBubble);
+ logBubbleEvent(mExpandedBubble, StatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
}
}
@@ -569,6 +593,9 @@
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SPRING_DAMPING_RATIO),
/* destination */ null);
+
+ logBubbleEvent(null /* no bubble associated with bubble stack move */,
+ StatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
/**
@@ -628,6 +655,7 @@
return;
}
+ mExpandedViewContainer.setEntry(mExpandedBubble.getEntry(), this);
if (mExpandedBubble.hasAppOverlayIntent()) {
// Bubble with activity view expanded state
ActivityView expandedView = mExpandedBubble.getActivityView();
@@ -635,8 +663,6 @@
expandedView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
- final PendingIntent intent = mExpandedBubble.getAppOverlayIntent();
- mExpandedViewContainer.setHeaderText(intent.getIntent().getComponent().toShortString());
mExpandedViewContainer.setExpandedView(expandedView);
} else {
// Bubble with notification view expanded state
@@ -650,8 +676,6 @@
} else {
mExpandedViewContainer.setExpandedView(null);
}
- // Bubble with notification as expanded state doesn't need a header / title
- mExpandedViewContainer.setHeaderText(null);
}
}
@@ -731,4 +755,72 @@
viewState.headsUpIsVisible = false;
viewState.applyToView(view);
}
+
+ /**
+ * @return the number of bubbles in the stack view.
+ */
+ private int getBubbleCount() {
+ return mBubbleContainer.getChildCount();
+ }
+
+ /**
+ * Finds the bubble index within the stack.
+ *
+ * @param bubbleView the view of the bubble.
+ * @return the index of the bubble view within the bubble stack. The range of the position
+ * is between 0 and the bubble count minus 1.
+ */
+ private int getBubbleIndex(BubbleView bubbleView) {
+ return mBubbleContainer.indexOfChild(bubbleView);
+ }
+
+ /**
+ * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
+ */
+ private float getNormalizedXPosition() {
+ return new BigDecimal(getPosition().x / mDisplaySize.x)
+ .setScale(4, RoundingMode.CEILING.HALF_UP)
+ .floatValue();
+ }
+
+ /**
+ * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
+ */
+ private float getNormalizedYPosition() {
+ return new BigDecimal(getPosition().y / mDisplaySize.y)
+ .setScale(4, RoundingMode.CEILING.HALF_UP)
+ .floatValue();
+ }
+
+ /**
+ * Logs the bubble UI event.
+ *
+ * @param bubble the bubble that is being interacted on. Null value indicates that
+ * the user interaction is not specific to one bubble.
+ * @param action the user interaction enum.
+ */
+ private void logBubbleEvent(@Nullable BubbleView bubble, int action) {
+ if (bubble == null) {
+ StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+ null /* package name */,
+ null /* notification channel */,
+ 0 /* notification ID */,
+ 0 /* bubble position */,
+ getBubbleCount(),
+ action,
+ getNormalizedXPosition(),
+ getNormalizedYPosition());
+ } else {
+ StatusBarNotification notification = bubble.getEntry().notification;
+ StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
+ notification.getPackageName(),
+ notification.getNotification().getChannelId(),
+ notification.getId(),
+ getBubbleIndex(bubble),
+ getBubbleCount(),
+ action,
+ getNormalizedXPosition(),
+ getNormalizedYPosition());
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index dc94832..2c23c0c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -37,6 +37,7 @@
import android.widget.TextView;
import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -59,6 +60,7 @@
private NotificationEntry mEntry;
private PendingIntent mAppOverlayIntent;
+ private BubbleController mBubbleController;
private ActivityView mActivityView;
private boolean mActivityViewReady;
private boolean mActivityViewStarted;
@@ -81,6 +83,7 @@
// XXX: can this padding just be on the view and we look it up?
mPadding = getResources().getDimensionPixelSize(R.dimen.bubble_view_padding);
mIconInset = getResources().getDimensionPixelSize(R.dimen.bubble_icon_inset);
+ mBubbleController = Dependency.get(BubbleController.class);
}
@Override
@@ -248,6 +251,20 @@
public void onActivityViewDestroyed(ActivityView view) {
mActivityViewReady = false;
}
+
+ /**
+ * This is only called for tasks on this ActivityView, which is also set to
+ * single-task mode -- meaning never more than one task on this display. If a task
+ * is being removed, it's the top Activity finishing and this bubble should
+ * be removed or collapsed.
+ */
+ @Override
+ public void onTaskRemovalStarted(int taskId) {
+ if (mEntry != null) {
+ // Must post because this is called from a binder thread.
+ post(() -> mBubbleController.removeBubble(mEntry.key));
+ }
+ }
});
}
return mActivityView;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index a784773..9bca3c7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,19 +35,19 @@
private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- private static final int REASONS = 10;
+ public static final int REASONS = 10;
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
public static final int PULSE_REASON_NOTIFICATION = 1;
public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
- public static final int PULSE_REASON_SENSOR_PICKUP = 3;
- public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
+ public static final int REASON_SENSOR_PICKUP = 3;
+ public static final int REASON_SENSOR_DOUBLE_TAP = 4;
public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
public static final int PULSE_REASON_DOCKING = 6;
public static final int REASON_SENSOR_WAKE_UP = 7;
public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 8;
- public static final int PULSE_REASON_SENSOR_TAP = 9;
+ public static final int REASON_SENSOR_TAP = 9;
private static boolean sRegisterKeyguardCallback = true;
@@ -202,13 +202,13 @@
case PULSE_REASON_INTENT: return "intent";
case PULSE_REASON_NOTIFICATION: return "notification";
case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
- case PULSE_REASON_SENSOR_PICKUP: return "pickup";
- case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
+ case REASON_SENSOR_PICKUP: return "pickup";
+ case REASON_SENSOR_DOUBLE_TAP: return "doubletap";
case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
case PULSE_REASON_DOCKING: return "docking";
case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakelockscreen";
case REASON_SENSOR_WAKE_UP: return "wakeup";
- case PULSE_REASON_SENSOR_TAP: return "tap";
+ case REASON_SENSOR_TAP: return "tap";
default: throw new IllegalArgumentException("bad reason: " + pulseReason);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 675948e..5efdc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -97,20 +97,20 @@
mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
Settings.Secure.DOZE_PICK_UP_GESTURE,
config.dozePickupSensorAvailable(),
- DozeLog.PULSE_REASON_SENSOR_PICKUP, false /* touchCoords */,
+ DozeLog.REASON_SENSOR_PICKUP, false /* touchCoords */,
false /* touchscreen */),
new TriggerSensor(
findSensorWithType(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
- DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+ DozeLog.REASON_SENSOR_DOUBLE_TAP,
dozeParameters.doubleTapReportsTouchCoordinates(),
true /* touchscreen */),
new TriggerSensor(
findSensorWithType(config.tapSensorType()),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
true /* configured */,
- DozeLog.PULSE_REASON_SENSOR_TAP,
+ DozeLog.REASON_SENSOR_TAP,
false /* reports touch coordinates */,
true /* touchscreen */),
new TriggerSensor(
@@ -530,7 +530,7 @@
/**
* Called when a sensor requests a pulse
- * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP}
+ * @param pulseReason Requesting sensor, e.g. {@link DozeLog#REASON_SENSOR_PICKUP}
* @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity.
* @param screenX the location on the screen where the sensor fired or -1
* if the sensor doesn't support reporting screen locations.
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index dc505b5..8a35e70 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -108,10 +108,6 @@
DozeLog.traceNotificationPulse(mContext);
}
- private void onWhisper() {
- requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
- }
-
private void proximityCheckThenCall(IntConsumer callback,
boolean alreadyPerformedProxCheck,
int reason) {
@@ -137,9 +133,9 @@
@VisibleForTesting
void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
float screenX, float screenY, float[] rawValues) {
- boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
- boolean isTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_TAP;
- boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
+ boolean isDoubleTap = pulseReason == DozeLog.REASON_SENSOR_DOUBLE_TAP;
+ boolean isTap = pulseReason == DozeLog.REASON_SENSOR_TAP;
+ boolean isPickup = pulseReason == DozeLog.REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
boolean isWakeDisplay = pulseReason == DozeLog.REASON_SENSOR_WAKE_UP;
boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index e2688f1..1cd3509 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.os.UserHandle
+import android.provider.Settings
import android.util.IconDrawableFactory
import android.view.Gravity
import android.view.LayoutInflater
@@ -64,7 +65,7 @@
setPositiveButton(R.string.ongoing_privacy_dialog_ok, null)
setNeutralButton(R.string.ongoing_privacy_dialog_open_settings,
object : DialogInterface.OnClickListener {
- val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).putExtra(
+ val intent = Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS).putExtra(
Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1))
@Suppress("DEPRECATION")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f0d804d..3db02b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -54,7 +54,7 @@
public static NotificationUiAdjustment extractFromNotificationEntry(
NotificationEntry entry) {
return new NotificationUiAdjustment(
- entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
+ entry.key, entry.systemGeneratedSmartActions, entry.systemGeneratedSmartReplies);
}
public static boolean needReinflate(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2e93c382..db9fcc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -102,7 +102,9 @@
/** Smart Actions provided by the NotificationAssistantService. */
@NonNull
public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
- public CharSequence[] smartReplies = new CharSequence[0];
+ /** Smart replies provided by the NotificationAssistantService. */
+ @NonNull
+ public CharSequence[] systemGeneratedSmartReplies = new CharSequence[0];
@VisibleForTesting
public int suppressedVisualEffects;
public boolean suspended;
@@ -182,7 +184,7 @@
userSentiment = ranking.getUserSentiment();
systemGeneratedSmartActions = ranking.getSmartActions() == null
? Collections.emptyList() : ranking.getSmartActions();
- smartReplies = ranking.getSmartReplies() == null
+ systemGeneratedSmartReplies = ranking.getSmartReplies() == null
? new CharSequence[0]
: ranking.getSmartReplies().toArray(new CharSequence[0]);
suppressedVisualEffects = ranking.getSuppressedVisualEffects();
@@ -239,6 +241,13 @@
}
/**
+ * Returns the data needed for a bubble for this notification, if it exists.
+ */
+ public Notification.BubbleMetadata getBubbleMetadata() {
+ return notification.getNotification().getBubbleMetadata();
+ }
+
+ /**
* Resets the notification entry to be re-used.
*/
public void reset() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0a04f4d..878d533 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1373,13 +1373,13 @@
}
// Apps didn't provide any smart replies / actions, use those from NAS (if any).
if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) {
- boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.smartReplies)
+ boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.systemGeneratedSmartReplies)
&& freeformRemoteInputActionPair != null
&& freeformRemoteInputActionPair.second.getAllowGeneratedReplies()
&& freeformRemoteInputActionPair.second.actionIntent != null;
if (useGeneratedReplies) {
smartReplies = new SmartReplyView.SmartReplies(
- entry.smartReplies,
+ entry.systemGeneratedSmartReplies,
freeformRemoteInputActionPair.first,
freeformRemoteInputActionPair.second.actionIntent,
true /* fromAssistant */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 302d630..304d2ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -47,7 +47,7 @@
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
/**
- * Mode in which we don't need to wake up the device when we get a fingerprint.
+ * Mode in which we don't need to wake up the device when we authenticate.
*/
public static final int MODE_NONE = 0;
@@ -70,8 +70,7 @@
public static final int MODE_SHOW_BOUNCER = 3;
/**
- * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
- * fingerprint.
+ * Mode in which we only wake up the device, and keyguard was not showing when we authenticated.
* */
public static final int MODE_ONLY_WAKE = 4;
@@ -96,22 +95,21 @@
*/
private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
- private final NotificationMediaManager mMediaManager =
- Dependency.get(NotificationMediaManager.class);
- private PowerManager mPowerManager;
- private Handler mHandler = new Handler();
+ private final NotificationMediaManager mMediaManager;
+ private final PowerManager mPowerManager;
+ private final Handler mHandler;
private PowerManager.WakeLock mWakeLock;
- private KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final UnlockMethodCache mUnlockMethodCache;
+ private final StatusBarWindowController mStatusBarWindowController;
+ private final Context mContext;
+ private final int mWakeUpDelay;
private int mMode;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private StatusBarWindowController mStatusBarWindowController;
private DozeScrimController mDozeScrimController;
private KeyguardViewMediator mKeyguardViewMediator;
private ScrimController mScrimController;
private StatusBar mStatusBar;
- private final UnlockMethodCache mUnlockMethodCache;
- private final Context mContext;
- private final int mWakeUpDelay;
private int mPendingAuthenticatedUserId = -1;
private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
private boolean mPendingShowBouncer;
@@ -122,11 +120,14 @@
KeyguardViewMediator keyguardViewMediator,
ScrimController scrimController,
StatusBar statusBar,
- UnlockMethodCache unlockMethodCache) {
+ UnlockMethodCache unlockMethodCache, Handler handler,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ int wakeUpDelay) {
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
- mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+ mUpdateMonitor = keyguardUpdateMonitor;
mUpdateMonitor.registerCallback(this);
+ mMediaManager = Dependency.get(NotificationMediaManager.class);
Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
@@ -135,8 +136,8 @@
mScrimController = scrimController;
mStatusBar = statusBar;
mUnlockMethodCache = unlockMethodCache;
- mWakeUpDelay = context.getResources().getInteger(
- com.android.internal.R.integer.config_wakeUpDelayDoze);
+ mHandler = handler;
+ mWakeUpDelay = wakeUpDelay;
}
public void setStatusBarKeyguardViewManager(
@@ -206,7 +207,7 @@
Trace.endSection();
return;
}
- startWakeAndUnlock(calculateMode());
+ startWakeAndUnlock(calculateMode(biometricSourceType));
}
public void startWakeAndUnlock(int mode) {
@@ -295,7 +296,7 @@
}
private void showBouncer() {
- if (calculateMode() == MODE_SHOW_BOUNCER) {
+ if (mMode == MODE_SHOW_BOUNCER) {
mStatusBarKeyguardViewManager.showBouncer(false);
}
mStatusBarKeyguardViewManager.animateCollapsePanels(
@@ -339,29 +340,30 @@
return mMode;
}
- private int calculateMode() {
+ private int calculateMode(BiometricSourceType biometricSourceType) {
boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
boolean deviceDreaming = mUpdateMonitor.isDreaming();
+ boolean isFace = biometricSourceType == BiometricSourceType.FACE;
if (!mUpdateMonitor.isDeviceInteractive()) {
if (!mStatusBarKeyguardViewManager.isShowing()) {
return MODE_ONLY_WAKE;
} else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
- return MODE_WAKE_AND_UNLOCK_PULSING;
+ return isFace ? MODE_NONE : MODE_WAKE_AND_UNLOCK_PULSING;
} else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
return MODE_WAKE_AND_UNLOCK;
} else {
return MODE_SHOW_BOUNCER;
}
}
- if (unlockingAllowed && deviceDreaming) {
+ if (unlockingAllowed && deviceDreaming && !isFace) {
return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
}
if (mStatusBarKeyguardViewManager.isShowing()) {
if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
return MODE_DISMISS_BOUNCER;
} else if (unlockingAllowed) {
- return MODE_UNLOCK;
+ return isFace ? MODE_ONLY_WAKE : MODE_UNLOCK;
} else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
return MODE_SHOW_BOUNCER;
}
@@ -437,7 +439,7 @@
}
/**
- * Successful authentication with fingerprint that wakes up the device.
+ * Successful authentication with fingerprint, face, or iris that wakes up the device.
*/
public boolean isWakeAndUnlock() {
return mMode == MODE_WAKE_AND_UNLOCK
@@ -446,7 +448,8 @@
}
/**
- * Successful authentication with fingerprint when the screen was either on or off.
+ * Successful authentication with fingerprint, face, or iris when the screen was either
+ * on or off.
*/
public boolean isBiometricUnlock() {
return isWakeAndUnlock() || mMode == MODE_UNLOCK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 8796e0a..c129143 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -354,6 +354,8 @@
protected StatusBarWindowController mStatusBarWindowController;
protected UnlockMethodCache mUnlockMethodCache;
@VisibleForTesting
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @VisibleForTesting
DozeServiceHost mDozeServiceHost = new DozeServiceHost();
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
@@ -674,7 +676,7 @@
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-
+ mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -761,7 +763,7 @@
mUnlockMethodCache.addListener(this);
startKeyguard();
- KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
+ mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
putComponent(DozeHost.class, mDozeServiceHost);
mScreenPinningRequest = new ScreenPinningRequest(mContext);
@@ -1206,7 +1208,9 @@
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mBiometricUnlockController = new BiometricUnlockController(mContext,
mDozeScrimController, keyguardViewMediator,
- mScrimController, this, UnlockMethodCache.getInstance(mContext));
+ mScrimController, this, UnlockMethodCache.getInstance(mContext),
+ new Handler(), mKeyguardUpdateMonitor, mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_wakeUpDelayDoze));
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
getBouncerContainer(), mNotificationPanel, mBiometricUnlockController);
mKeyguardIndicationController
@@ -2357,8 +2361,8 @@
mLightBarController.dump(fd, pw, args);
}
- if (KeyguardUpdateMonitor.getInstance(mContext) != null) {
- KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
+ if (mKeyguardUpdateMonitor != null) {
+ mKeyguardUpdateMonitor.dump(fd, pw, args);
}
FalsingManager.getInstance(mContext).dump(pw);
@@ -3891,6 +3895,11 @@
return;
}
+ if (mKeyguardUpdateMonitor != null
+ && reason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mKeyguardUpdateMonitor.onAuthInterruptDetected();
+ }
+
// Set the state to pulsing, so ScrimController will know what to do once we ask it to
// execute the transition. The pulse callback will then be invoked when the scrims
// are black, indicating that StatusBar is ready to present the rest of the UI.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2055519..d5a275e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -18,15 +18,33 @@
import static com.google.common.truth.Truth.assertThat;
-import com.google.common.truth.Truth.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
+import android.app.trust.TrustManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
+import android.os.UserManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -39,6 +57,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -52,37 +72,71 @@
@RunWithLooper(setAsMainLooper = true)
public class KeyguardUpdateMonitorTest extends SysuiTestCase {
+ @Mock
+ private KeyguardUpdateMonitor.StrongAuthTracker mStrongAuthTracker;
+ @Mock
+ private TrustManager mTrustManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+ @Mock
+ private BiometricManager mBiometricManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
private TestableLooper mTestableLooper;
+ private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+ TestableContext context = spy(mContext);
+ when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
+ when(context.getPackageManager()).thenReturn(mPackageManager);
+ doAnswer(invocation -> {
+ IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
+ callback.onChanged(BiometricSourceType.FACE, true /* enabled */);
+ return null;
+ }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ context.addMockSystemService(TrustManager.class, mTrustManager);
+ context.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+ context.addMockSystemService(BiometricManager.class, mBiometricManager);
+ context.addMockSystemService(FaceManager.class, mFaceManager);
+ context.addMockSystemService(UserManager.class, mUserManager);
+ context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+
mTestableLooper = TestableLooper.get(this);
+ mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
}
@Test
public void testIgnoresSimStateCallback_rebroadcast() {
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
-
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
Assert.assertTrue("onSimStateChanged not called",
- keyguardUpdateMonitor.hasSimStateJustChanged());
+ mKeyguardUpdateMonitor.hasSimStateJustChanged());
intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
Assert.assertFalse("onSimStateChanged should have been skipped",
- keyguardUpdateMonitor.hasSimStateJustChanged());
+ mKeyguardUpdateMonitor.hasSimStateJustChanged());
}
@Test
public void testTelephonyCapable_BootInitState() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
}
@Test
@@ -90,36 +144,30 @@
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent,null, false));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
}
@Test
public void testTelephonyCapable_SimInvalid_ServiceState_InService() {
// SERVICE_STATE - IN_SERVICE, but SIM_STATE is invalid TelephonyCapable should be False
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
state.fillInNotifierBundle(data);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, false));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
}
@Test
public void testTelephonyCapable_SimValid_ServiceState_PowerOff() {
// Simulate AirplaneMode case, SERVICE_STATE - POWER_OFF, check TelephonyCapable False
// Only receive ServiceState callback IN_SERVICE -> OUT_OF_SERVICE -> POWER_OFF
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
, IccCardConstants.INTENT_VALUE_ICC_LOADED);
@@ -127,10 +175,10 @@
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_POWER_OFF);
state.fillInNotifierBundle(data);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, true));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
}
/* Normal SIM inserted flow
@@ -142,24 +190,20 @@
*/
@Test
public void testTelephonyCapable_BootInitState_ServiceState_OutOfService() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_OUT_OF_SERVICE);
state.fillInNotifierBundle(data);
intent.putExtras(data);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, false));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
}
@Test
public void testTelephonyCapable_BootInitState_SimState_NotReady() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -167,16 +211,14 @@
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
, IccCardConstants.INTENT_VALUE_ICC_NOT_READY);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, false));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
}
@Test
public void testTelephonyCapable_BootInitState_SimState_Ready() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -184,46 +226,40 @@
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
, IccCardConstants.INTENT_VALUE_ICC_READY);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, false));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
}
@Test
public void testTelephonyCapable_BootInitState_ServiceState_PowerOff() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_POWER_OFF);
state.fillInNotifierBundle(data);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, false));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
}
@Test
public void testTelephonyCapable_SimValid_ServiceState_InService() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
state.fillInNotifierBundle(data);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intent, data, true));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
}
@Test
public void testTelephonyCapable_SimValid_SimState_Loaded() {
- TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
- new TestableKeyguardUpdateMonitor(getContext());
Bundle data = new Bundle();
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
@@ -231,19 +267,96 @@
Intent intentSimState = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intentSimState.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
, IccCardConstants.INTENT_VALUE_ICC_LOADED);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intentSimState, data, true));
mTestableLooper.processAllMessages();
// Even SimState Loaded, still need ACTION_SERVICE_STATE_CHANGED turn on mTelephonyCapable
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isFalse();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
Intent intentServiceState = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
intentSimState.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE
, IccCardConstants.INTENT_VALUE_ICC_LOADED);
- keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
, putPhoneInfo(intentServiceState, data, true));
mTestableLooper.processAllMessages();
- assertThat(keyguardUpdateMonitor.mTelephonyCapable).isTrue();
+ assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isTrue();
+ }
+
+ @Test
+ public void testTriesToAuthenticate_whenBouncer() {
+ mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
+ mTestableLooper.processAllMessages();
+
+ verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any());
+ verify(mFaceManager).isHardwareDetected();
+ verify(mFaceManager).hasEnrolledTemplates(anyInt());
+ }
+
+ @Test
+ public void testTriesToAuthenticate_whenKeyguard() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+ verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ public void skipsAuthentication_whenEncryptedKeyguard() {
+ reset(mUserManager);
+ when(mUserManager.isUserUnlocked(anyInt())).thenReturn(false);
+
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+ verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ public void testTriesToAuthenticate_whenAssistant() {
+ mKeyguardUpdateMonitor.setKeyguardOccluded(true);
+ mKeyguardUpdateMonitor.setAssistantVisible(true);
+
+ verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ public void testNeverAuthenticates_whenFaceLockout() {
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+ .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT, "lockout");
+ mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
+ mTestableLooper.processAllMessages();
+
+ verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
+ mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
+ mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
+ mTestableLooper.processAllMessages();
+
+ verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+ }
+
+ @Test
+ public void testGetUserCanSkipBouncer_whenFace() {
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onFaceAuthenticated(user);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
+ }
+
+ @Test
+ public void testGetUserCanSkipBouncer_whenFingerprint() {
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(user);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
+ }
+
+ @Test
+ public void testGetUserCanSkipBouncer_whenTrust() {
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) {
@@ -261,8 +374,9 @@
protected TestableKeyguardUpdateMonitor(Context context) {
super(context);
- // Avoid race condition when unexpected broadcast could be received.
context.unregisterReceiver(mBroadcastReceiver);
+ context.unregisterReceiver(mBroadcastAllReceiver);
+ mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker;
}
public boolean hasSimStateJustChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index e1c481e..5a6200f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -230,9 +230,9 @@
public void testPulseReason_getMatchesRequest() {
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
- mMachine.requestPulse(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP);
+ mMachine.requestPulse(DozeLog.REASON_SENSOR_DOUBLE_TAP);
- assertEquals(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
+ assertEquals(DozeLog.REASON_SENSOR_DOUBLE_TAP, mMachine.getPulseReason());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 7b358b9..cdac7c97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -125,7 +125,7 @@
public void testOnSensor_whenUndockedWithNearAndDoubleTapScreen_shouldNotWakeUp() {
mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
- mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP,
false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
null /* rawValues */);
@@ -137,7 +137,7 @@
doReturn(true).when(mDockManagerFake).isDocked();
mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
- mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP,
false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
null /* rawValues */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index c36fec2..c80396d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -209,7 +209,8 @@
@Test
public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() {
- mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ mEntry.systemGeneratedSmartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
mEntry.systemGeneratedSmartActions =
createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
when(mSmartReplyConstants.isEnabled()).thenReturn(false);
@@ -256,11 +257,13 @@
// replies.
setupAppGeneratedReplies(null /* smartReplies */);
- mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ mEntry.systemGeneratedSmartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
- assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.smartReplies);
+ assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
+ mEntry.systemGeneratedSmartReplies);
assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
assertThat(repliesAndActions.smartActions).isNull();
}
@@ -271,7 +274,7 @@
// replies.
setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
- mEntry.smartReplies =
+ mEntry.systemGeneratedSmartReplies =
new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
NotificationContentView.SmartRepliesAndActions repliesAndActions =
NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
@@ -306,7 +309,8 @@
createActions(new String[] {"Test Action 1", "Test Action 2"});
setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions);
- mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ mEntry.systemGeneratedSmartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
mEntry.systemGeneratedSmartActions =
createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
@@ -325,7 +329,8 @@
// actions.
setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */);
when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false);
- mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ mEntry.systemGeneratedSmartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
mEntry.systemGeneratedSmartActions =
createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
new file mode 100644
index 0000000..7be4756
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 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.systemui.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.statusbar.NotificationMediaManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class BiometricsUnlockControllerTest extends SysuiTestCase {
+
+ @Mock
+ private NotificationMediaManager mMediaManager;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private KeyguardUpdateMonitor mUpdateMonitor;
+ @Mock
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock
+ private StatusBarWindowController mStatusBarWindowController;
+ @Mock
+ private DozeScrimController mDozeScrimController;
+ @Mock
+ private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock
+ private ScrimController mScrimController;
+ @Mock
+ private StatusBar mStatusBar;
+ @Mock
+ private UnlockMethodCache mUnlockMethodCache;
+ private BiometricUnlockController mBiometricUnlockController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+ mContext.addMockSystemService(PowerManager.class, mPowerManager);
+ mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
+ mDependency.injectTestDependency(StatusBarWindowController.class,
+ mStatusBarWindowController);
+ mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController,
+ mKeyguardViewMediator, mScrimController, mStatusBar, mUnlockMethodCache,
+ new Handler(), mUpdateMonitor, 0 /* wakeUpDelay */);
+ mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() {
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT);
+ verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
+ verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFingerprintAndNotInteractive_wakeAndUnlock() {
+ reset(mUpdateMonitor);
+ reset(mStatusBarKeyguardViewManager);
+ when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mDozeScrimController.isPulsing()).thenReturn(true);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT);
+
+ verify(mKeyguardViewMediator).onWakeAndUnlocking();
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT);
+
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT);
+
+ verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFace_dontDismissKeyguard() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE);
+
+ verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE);
+
+ verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFaceAndPulsing_dontDismissKeyguard() {
+ reset(mUpdateMonitor);
+ reset(mStatusBarKeyguardViewManager);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mDozeScrimController.isPulsing()).thenReturn(true);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FACE);
+
+ verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 17611ff..49fcafd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -64,6 +64,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.InitController;
@@ -120,6 +121,9 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -161,6 +165,8 @@
private NotificationAlertingManager mNotificationAlertingManager;
@Mock
private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableStatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
@@ -252,7 +258,7 @@
mDozeScrimController, mock(NotificationShelf.class),
mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
mock(BubbleController.class), mock(NavigationBarController.class),
- mock(AutoHideController.class));
+ mock(AutoHideController.class), mKeyguardUpdateMonitor);
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
SystemUIFactory.getInstance().getRootComponent()
@@ -631,6 +637,39 @@
}
@Test
+ public void testPulseWhileDozing_notifyAuthInterrupt() {
+ HashSet<Integer> reasonsWantingAuth = new HashSet<>(
+ Collections.singletonList(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
+ HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
+ Arrays.asList(DozeLog.PULSE_REASON_INTENT,
+ DozeLog.PULSE_REASON_NOTIFICATION,
+ DozeLog.PULSE_REASON_SENSOR_SIGMOTION,
+ DozeLog.REASON_SENSOR_PICKUP,
+ DozeLog.REASON_SENSOR_DOUBLE_TAP,
+ DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
+ DozeLog.PULSE_REASON_DOCKING,
+ DozeLog.REASON_SENSOR_WAKE_UP,
+ DozeLog.REASON_SENSOR_TAP));
+ HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
+ Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
+ DozeLog.REASON_SENSOR_DOUBLE_TAP,
+ DozeLog.REASON_SENSOR_TAP));
+
+ for (int i = 0; i < DozeLog.REASONS; i++) {
+ reset(mKeyguardUpdateMonitor);
+ mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
+ if (reasonsWantingAuth.contains(i)) {
+ verify(mKeyguardUpdateMonitor).onAuthInterruptDetected();
+ } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) {
+ verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected();
+ } else {
+ throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping"
+ + " passive auth. Please consider how this pulse reason should behave.");
+ }
+ }
+ }
+
+ @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
@@ -705,7 +744,8 @@
NotificationPresenter notificationPresenter,
BubbleController bubbleController,
NavigationBarController navBarController,
- AutoHideController autoHideController) {
+ AutoHideController autoHideController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mStatusBarKeyguardViewManager = man;
mUnlockMethodCache = unlock;
mKeyguardIndicationController = key;
@@ -738,6 +778,7 @@
mBubbleController = bubbleController;
mNavigationBarController = navBarController;
mAutoHideController = autoHideController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
}
private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index aeb4261..fceb656 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6915,6 +6915,11 @@
// OS: Q
MOBILE_NETWORK_RENAME_DIALOG = 1642;
+ // ACTION: Settings > Search Bar > Avatar
+ // CATEGORY: SETTINGS
+ // OS: Q
+ ACTION_CLICK_ACCOUNT_AVATAR = 1643;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index b84736b..2b45b49 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1888,11 +1888,31 @@
LABEL_BAD = 2;
}
+ enum UsabilityStatsTriggerType {
+ // Default/Invalid event
+ TYPE_UNKNOWN = 0;
+
+ // There is a data stall from tx failures
+ TYPE_DATA_STALL_BAD_TX = 1;
+
+ // There is a data stall from rx failures
+ TYPE_DATA_STALL_TX_WITHOUT_RX = 2;
+
+ // There is a data stall from both tx and rx failures
+ TYPE_DATA_STALL_BOTH = 3;
+
+ // Firmware generated an alert
+ TYPE_FIRMWARE_ALERT = 4;
+ }
+
// The current wifi usability state
optional Label label = 1;
// The list of timestamped wifi usability stats
repeated WifiUsabilityStatsEntry stats = 2;
+
+ // What event triggered WifiUsabilityStats.
+ optional UsabilityStatsTriggerType trigger_type = 3;
}
message DeviceMobilityStatePnoScanStats {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index bf08f3e..f9de554 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -50,7 +50,7 @@
"android.hardware.vibrator-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
- "android.hidl.manager-V1.0-java",
+ "android.hidl.manager-V1.2-java",
"netd_aidl_interface-java",
"netd_event_listener_interface-java",
],
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d95604e..2f1f91e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
@@ -218,7 +219,7 @@
if (DEBUG_FOREGROUND_SERVICE) {
Slog.i(TAG, " Stopping fg for service " + r);
}
- setServiceForegroundInnerLocked(r, 0, null, 0);
+ setServiceForegroundInnerLocked(r, 0, null, 0, 0);
}
}
}
@@ -914,13 +915,13 @@
}
public void setServiceForegroundLocked(ComponentName className, IBinder token,
- int id, Notification notification, int flags) {
+ int id, Notification notification, int flags, int foregroundServiceType) {
final int userId = UserHandle.getCallingUserId();
final long origId = Binder.clearCallingIdentity();
try {
ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
- setServiceForegroundInnerLocked(r, id, notification, flags);
+ setServiceForegroundInnerLocked(r, id, notification, flags, foregroundServiceType);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -1211,7 +1212,7 @@
* @param id Notification ID. Zero === exit foreground state for the given service.
*/
private void setServiceForegroundInnerLocked(final ServiceRecord r, int id,
- Notification notification, int flags) {
+ Notification notification, int flags, int foregroundServiceType) {
if (id != 0) {
if (notification == null) {
throw new IllegalArgumentException("null notification");
@@ -1244,13 +1245,20 @@
android.Manifest.permission.FOREGROUND_SERVICE,
r.app.pid, r.appInfo.uid, "startForeground");
}
- if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
- if (r.serviceInfo.getForegroundServiceType()
- == ServiceInfo.FOREGROUND_SERVICE_TYPE_UNSPECIFIED) {
- // STOPSHIP(b/120611119): replace log message with SecurityException.
- Slog.w(TAG, "missing foregroundServiceType attribute in "
- + "service element of manifest file");
- }
+
+ int manifestType = r.serviceInfo.getForegroundServiceType();
+ // If passed in foreground service type is FOREGROUND_SERVICE_TYPE_MANIFEST,
+ // consider it is the same as manifest foreground service type.
+ if (foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST) {
+ foregroundServiceType = manifestType;
+ }
+ // Check the passed in foreground service type flags is a subset of manifest
+ // foreground service type flags.
+ if ((foregroundServiceType & manifestType) != foregroundServiceType) {
+ // STOPSHIP(b/120611119): replace log message with IllegalArgumentException.
+ Slog.w(TAG, "foregroundServiceType must be a subset of "
+ + "foregroundServiceType attribute in "
+ + "service element of manifest file");
}
}
boolean alreadyStartedOp = false;
@@ -1307,6 +1315,7 @@
}
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
r.foregroundNoti = notification;
+ r.foregroundServiceType = foregroundServiceType;
if (!r.isForeground) {
final ServiceMap smap = getServiceMapLocked(r.userId);
if (smap != null) {
@@ -1443,7 +1452,7 @@
ServiceRecord sr = proc.services.valueAt(i);
if (sr.isForeground || sr.fgRequired) {
anyForeground = true;
- fgServiceTypes |= sr.serviceInfo.mForegroundServiceType;
+ fgServiceTypes |= sr.foregroundServiceType;
break;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 66e9eb3a..c016c89 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -549,6 +549,11 @@
// Whether we should use SCHED_FIFO for UI and RenderThreads.
boolean mUseFifoUiScheduling = false;
+ // Use an offload queue for long broadcasts, e.g. BOOT_COMPLETED.
+ // For simplicity, since we statically declare the size of the array of BroadcastQueues,
+ // we still create this new offload queue, but never ever put anything on it.
+ boolean mEnableOffloadQueue;
+
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
BroadcastQueue mOffloadBroadcastQueue;
@@ -2283,6 +2288,9 @@
// by default, no "slow" policy in this queue
offloadConstants.SLOW_TIME = Integer.MAX_VALUE;
+ mEnableOffloadQueue = SystemProperties.getBoolean(
+ "persist.device_config.activity_manager_native_boot.offload_queue_enabled", false);
+
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
@@ -4496,6 +4504,7 @@
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
+ long bindApplicationTimeMillis;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
@@ -4733,6 +4742,7 @@
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
+ bindApplicationTimeMillis = SystemClock.elapsedRealtime();
mAtmInternal.preBindApplication(app.getWindowProcessController());
final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
if (app.isolatedEntryPoint != null) {
@@ -4854,6 +4864,17 @@
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
+ StatsLog.write(
+ StatsLog.PROCESS_START_TIME,
+ app.info.uid,
+ app.pid,
+ app.info.packageName,
+ StatsLog.PROCESS_START_TIME__TYPE__COLD,
+ app.startTime,
+ (int) (bindApplicationTimeMillis - app.startTime),
+ (int) (SystemClock.elapsedRealtime() - app.startTime),
+ app.hostingType,
+ (app.hostingNameStr != null ? app.hostingNameStr : ""));
return true;
}
@@ -13530,9 +13551,10 @@
@Override
public void setServiceForeground(ComponentName className, IBinder token,
- int id, Notification notification, int flags) {
+ int id, Notification notification, int flags, int foregroundServiceType) {
synchronized(this) {
- mServices.setServiceForegroundLocked(className, token, id, notification, flags);
+ mServices.setServiceForegroundLocked(className, token, id, notification, flags,
+ foregroundServiceType);
}
}
@@ -14090,7 +14112,8 @@
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, false, null, null, OP_NONE, null, receivers,
- null, 0, null, null, false, true, true, -1, false);
+ null, 0, null, null, false, true, true, -1, false,
+ false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
@@ -14475,6 +14498,8 @@
}
}
+ boolean timeoutExempt = false;
+
if (action != null) {
if (getBackgroundLaunchBroadcasts().contains(action)) {
if (DEBUG_BACKGROUND_CHECK) {
@@ -14700,6 +14725,9 @@
Log.w(TAG, "Broadcast " + action
+ " no longer supported. It will not be delivered.");
return ActivityManager.BROADCAST_SUCCESS;
+ case Intent.ACTION_PRE_BOOT_COMPLETED:
+ timeoutExempt = true;
+ break;
}
if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
@@ -14842,7 +14870,7 @@
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
resultCode, resultData, resultExtras, ordered, sticky, false, userId,
- allowBackgroundActivityStarts);
+ allowBackgroundActivityStarts, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending
&& (queue.replaceParallelBroadcastLocked(r) != null);
@@ -14939,7 +14967,7 @@
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId,
- allowBackgroundActivityStarts);
+ allowBackgroundActivityStarts, timeoutExempt);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
@@ -18389,6 +18417,6 @@
}
private boolean isOnOffloadQueue(int flags) {
- return ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0);
+ return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0));
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 1fb11ba..a11ebfd 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -461,14 +461,21 @@
// if this receiver was slow, impose deferral policy on the app. This will kick in
// when processNextBroadcastLocked() next finds this uid as a receiver identity.
- if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r);
+ if (!r.timeoutExempt) {
+ if (mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG, "Broadcast receiver was slow: " + receiver + " br=" + r);
+ }
+ if (r.curApp != null) {
+ mDispatcher.startDeferring(r.curApp.uid);
+ } else {
+ Slog.d(TAG, "finish receiver curApp is null? " + r);
+ }
}
- if (r.curApp != null) {
- mDispatcher.startDeferring(r.curApp.uid);
- } else {
- Slog.d(TAG, "finish receiver curApp is null? " + r);
+ } else {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
+ + " is exempt from deferral policy");
}
}
@@ -1008,12 +1015,11 @@
// detection, we catch "hung" broadcasts here, discard them,
// and continue to make progress.
//
- // This is only done if the system is ready so that PRE_BOOT_COMPLETED
- // receivers don't get executed with timeouts. They're intended for
- // one time heavy lifting after system upgrades and can take
- // significant amounts of time.
+ // This is only done if the system is ready so that early-stage receivers
+ // don't get executed with timeouts; and of course other timeout-
+ // exempt broadcasts are ignored.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (mService.mProcessesReady && r.dispatchTime > 0) {
+ if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
Slog.w(TAG, "Hung broadcast ["
@@ -1619,9 +1625,17 @@
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
if (!mService.mProcessesReady) {
- // Only process broadcast timeouts if the system is ready. That way
- // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
- // to do heavy lifting for system up.
+ // Only process broadcast timeouts if the system is ready; some early
+ // broadcasts do heavy work setting up system facilities
+ return;
+ }
+
+ // If the broadcast is generally exempt from timeout tracking, we're done
+ if (r.timeoutExempt) {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ + r.intent.getAction());
+ }
return;
}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index d9e03f8..fa9b79d 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -74,6 +74,7 @@
long dispatchClockTime; // the clock time the dispatch started
long receiverTime; // when current receiver started for timeouts.
long finishTime; // when we finished the broadcast.
+ boolean timeoutExempt; // true if this broadcast is not subject to receiver timeouts
int resultCode; // current result code value.
String resultData; // current result data value.
Bundle resultExtras; // current result extra data values.
@@ -236,7 +237,7 @@
String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
- boolean _allowBackgroundActivityStarts) {
+ boolean _allowBackgroundActivityStarts, boolean _timeoutExempt) {
if (_intent == null) {
throw new NullPointerException("Can't construct with a null intent");
}
@@ -266,6 +267,7 @@
nextReceiver = 0;
state = IDLE;
allowBackgroundActivityStarts = _allowBackgroundActivityStarts;
+ timeoutExempt = _timeoutExempt;
}
/**
@@ -310,6 +312,7 @@
manifestSkipCount = from.manifestSkipCount;
queue = from.queue;
allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
+ timeoutExempt = from.timeoutExempt;
}
/**
@@ -345,7 +348,7 @@
callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, initialSticky, userId,
- allowBackgroundActivityStarts);
+ allowBackgroundActivityStarts, timeoutExempt);
return split;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 49c4bc4..f90c0ca 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -58,7 +58,6 @@
import android.graphics.Point;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
-import android.net.Uri;
import android.os.AppZygote;
import android.os.Binder;
import android.os.Build;
@@ -1481,6 +1480,9 @@
mService.mSafeMode == true) {
runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
+ if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0) {
+ runtimeFlags |= Zygote.PROFILE_FROM_SHELL;
+ }
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
@@ -1507,7 +1509,7 @@
mService.mNativeDebuggingApp = null;
}
- if (app.info.isCodeIntegrityPreferred()
+ if (app.info.isEmbeddedDexUsed()
|| (app.info.isPrivilegedApp()
&& DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index da5ce1c..abc1066a 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -16,10 +16,8 @@
package com.android.server.am;
-import com.android.internal.app.procstats.ServiceState;
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.LocalServices;
-import com.android.server.notification.NotificationManagerInternal;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.INotificationManager;
import android.app.Notification;
@@ -44,6 +42,11 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+
+import com.android.internal.app.procstats.ServiceState;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriPermissionOwner;
@@ -52,9 +55,6 @@
import java.util.List;
import java.util.Objects;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
/**
* A running application service.
*/
@@ -103,6 +103,7 @@
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
+ int foregroundServiceType; // foreground service types.
long lastActivity; // last time there was some activity on the service.
long startingBgTimeout; // time at which we scheduled this for a delayed start.
boolean startRequested; // someone explicitly called start?
@@ -722,7 +723,7 @@
// If it gave us a garbage notification, it doesn't
// get to be foreground.
ams.setServiceForeground(instanceName, ServiceRecord.this,
- 0, null, 0);
+ 0, null, 0, 0);
ams.crashApplication(appUid, appPid, localPackageName, -1,
"Bad notification for startForeground: " + e);
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 15d66e6..8cc560e 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -719,14 +719,17 @@
// result back to the client.
// TODO(b/123378871): Remove when moved.
if (bundle.getBoolean(BiometricPrompt.KEY_ENABLE_FALLBACK)) {
- mConfirmDeviceCredentialReceiver = receiver;
- final KeyguardManager kgm = getContext().getSystemService(KeyguardManager.class);
- // Use this so we don't need to duplicate logic..
- final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
- null /* description */);
- // Then give it the bundle to do magic behavior..
- intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
- getContext().startActivityAsUser(intent, UserHandle.CURRENT);
+ mHandler.post(() -> {
+ mConfirmDeviceCredentialReceiver = receiver;
+ final KeyguardManager kgm = getContext().getSystemService(
+ KeyguardManager.class);
+ // Use this so we don't need to duplicate logic..
+ final Intent intent = kgm.createConfirmDeviceCredentialIntent(null /* title */,
+ null /* description */);
+ // Then give it the bundle to do magic behavior..
+ intent.putExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE, bundle);
+ getContext().startActivityAsUser(intent, UserHandle.CURRENT);
+ });
return;
}
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index eb0ed0a..591ce8d 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -55,6 +55,7 @@
import android.opengl.Matrix;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
@@ -66,6 +67,9 @@
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.DisplayPrimaries;
+
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ColorDisplayController;
@@ -145,43 +149,20 @@
@Override
public void setUp(Context context, boolean needsLinear) {
mSetUp = false;
+ final Resources res = context.getResources();
- final Resources res = getContext().getResources();
- final String[] displayPrimariesValues = res.getStringArray(
- R.array.config_displayWhiteBalanceDisplayPrimaries);
+ ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
+ if (displayColorSpaceRGB == null) {
+ Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
+ displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
+ if (displayColorSpaceRGB == null) {
+ Slog.e(TAG, "Failed to get display color space from resources");
+ return;
+ }
+ }
+
final String[] nominalWhiteValues = res.getStringArray(
R.array.config_displayWhiteBalanceDisplayNominalWhite);
-
- if (displayPrimariesValues.length != NUM_DISPLAY_PRIMARIES_VALS) {
- Slog.e(TAG, "Unexpected display white balance primaries resource length " +
- displayPrimariesValues.length);
- return;
- }
-
- if (nominalWhiteValues.length != NUM_VALUES_PER_PRIMARY) {
- Slog.e(TAG, "Unexpected display white balance nominal white resource length " +
- nominalWhiteValues.length);
- return;
- }
-
- float[] displayRedGreenBlueXYZ =
- new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
- float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
- for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
- displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
- }
- for (int i = 0; i < displayWhiteXYZ.length; i++) {
- displayWhiteXYZ[i] = Float.parseFloat(
- displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
- }
-
- final ColorSpace.Rgb displayColorSpaceRGB = new ColorSpace.Rgb(
- "Display Color Space",
- displayRedGreenBlueXYZ,
- displayWhiteXYZ,
- 2.2f // gamma, unused for display white balance
- );
-
float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
for (int i = 0; i < nominalWhiteValues.length; i++) {
displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
@@ -190,14 +171,14 @@
final int colorTemperatureMin = res.getInteger(
R.integer.config_displayWhiteBalanceColorTemperatureMin);
if (colorTemperatureMin <= 0) {
- Slog.e(TAG, "display white balance minimum temperature must be greater than 0");
+ Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
return;
}
final int colorTemperatureMax = res.getInteger(
R.integer.config_displayWhiteBalanceColorTemperatureMax);
if (colorTemperatureMax < colorTemperatureMin) {
- Slog.e(TAG, "display white balance max temp must be greater or equal to min");
+ Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
return;
}
@@ -323,6 +304,57 @@
matrixToString(mMatrixDisplayWhiteBalance, 4));
}
}
+
+ private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
+ return new ColorSpace.Rgb(
+ "Display Color Space",
+ redGreenBlueXYZ,
+ whiteXYZ,
+ 2.2f // gamma, unused for display white balance
+ );
+ }
+
+ private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
+ IBinder displayToken = SurfaceControl.getBuiltInDisplay(
+ SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
+ if (displayToken == null) {
+ return null;
+ }
+
+ DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
+ if (primaries == null || primaries.red == null || primaries.green == null ||
+ primaries.blue == null || primaries.white == null) {
+ return null;
+ }
+
+ return makeRgbColorSpaceFromXYZ(
+ new float[] {
+ primaries.red.X, primaries.red.Y, primaries.red.Z,
+ primaries.green.X, primaries.green.Y, primaries.green.Z,
+ primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
+ },
+ new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
+ );
+ }
+
+ private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
+ final String[] displayPrimariesValues = res.getStringArray(
+ R.array.config_displayWhiteBalanceDisplayPrimaries);
+ float[] displayRedGreenBlueXYZ =
+ new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+ float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+
+ for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+ displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+ }
+
+ for (int i = 0; i < displayWhiteXYZ.length; i++) {
+ displayWhiteXYZ[i] = Float.parseFloat(
+ displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+ }
+
+ return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
+ }
};
private final TintController mGlobalSaturationTintController = new TintController() {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c9df86e..80ea1da 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2196,6 +2196,22 @@
}
}
+ void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
+ }
+ }
+ }
+
+ void setAmbientColorTemperatureOverride(float cct) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAmbientColorTemperatureOverride(cct);
+ }
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index abbfc7b..04d28ea 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -45,6 +45,12 @@
return setAutoBrightnessLoggingEnabled(true);
case "ab-logging-disable":
return setAutoBrightnessLoggingEnabled(false);
+ case "dwb-logging-enable":
+ return setDisplayWhiteBalanceLoggingEnabled(true);
+ case "dwb-logging-disable":
+ return setDisplayWhiteBalanceLoggingEnabled(false);
+ case "dwb-set-cct":
+ return setAmbientColorTemperatureOverride();
default:
return handleDefaultCommands(cmd);
}
@@ -65,6 +71,12 @@
pw.println(" Enable auto-brightness logging.");
pw.println(" ab-logging-disable");
pw.println(" Disable auto-brightness logging.");
+ pw.println(" dwb-logging-enable");
+ pw.println(" Enable display white-balance logging.");
+ pw.println(" dwb-logging-disable");
+ pw.println(" Disable display white-balance logging.");
+ pw.println(" dwb-set-cct CCT");
+ pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable).");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -75,7 +87,7 @@
getErrPrintWriter().println("Error: no brightness specified");
return 1;
}
- float brightness = -1;
+ float brightness = -1.0f;
try {
brightness = Float.parseFloat(brightnessText);
} catch (NumberFormatException e) {
@@ -84,7 +96,7 @@
getErrPrintWriter().println("Error: brightness should be a number between 0 and 1");
return 1;
}
- mService.setBrightness((int) brightness * 255);
+ mService.setBrightness((int) (brightness * 255));
return 0;
}
@@ -97,4 +109,26 @@
mService.setAutoBrightnessLoggingEnabled(enabled);
return 0;
}
+
+ private int setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ mService.setDisplayWhiteBalanceLoggingEnabled(enabled);
+ return 0;
+ }
+
+ private int setAmbientColorTemperatureOverride() {
+ String cctText = getNextArg();
+ if (cctText == null) {
+ getErrPrintWriter().println("Error: no cct specified");
+ return 1;
+ }
+ float cct;
+ try {
+ cct = Float.parseFloat(cctText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: cct should be a number");
+ return 1;
+ }
+ mService.setAmbientColorTemperatureOverride(cct);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2a8462b..dc5be6a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -52,6 +52,9 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
@@ -78,7 +81,8 @@
* For debugging, you can make the color fade and brightness animations run
* slower by changing the "animator duration scale" option in Development Settings.
*/
-final class DisplayPowerController implements AutomaticBrightnessController.Callbacks {
+final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
+ DisplayWhiteBalanceController.Callbacks {
private static final String TAG = "DisplayPowerController";
private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
@@ -307,6 +311,12 @@
// Whether or not to skip the initial brightness ramps into STATE_ON.
private final boolean mSkipScreenOnBrightnessRamp;
+ // Display white balance components.
+ @Nullable
+ private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
+ @Nullable
+ private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+
// A record of state for skipping brightness ramps.
private int mSkipRampState = RAMP_STATE_SKIP_NONE;
@@ -504,6 +514,20 @@
mPendingScreenBrightnessSetting = -1;
mTemporaryAutoBrightnessAdjustment = Float.NaN;
mPendingAutoBrightnessAdjustment = Float.NaN;
+
+ DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
+ DisplayWhiteBalanceController displayWhiteBalanceController = null;
+ try {
+ displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
+ displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
+ mSensorManager, resources);
+ displayWhiteBalanceSettings.setCallbacks(this);
+ displayWhiteBalanceController.setCallbacks(this);
+ } catch (Exception e) {
+ Slog.e(TAG, "failed to set up display white-balance: " + e);
+ }
+ mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
+ mDisplayWhiteBalanceController = displayWhiteBalanceController;
}
/**
@@ -526,6 +550,9 @@
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
mBrightnessTracker.onSwitchUser(newUserId);
+ if (mDisplayWhiteBalanceSettings != null) {
+ mDisplayWhiteBalanceSettings.onSwitchUser();
+ }
}
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -988,6 +1015,16 @@
}
+ // Update display white-balance.
+ if (mDisplayWhiteBalanceController != null) {
+ if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
+ mDisplayWhiteBalanceController.setEnabled(true);
+ mDisplayWhiteBalanceController.updateScreenColorTemperature();
+ } else {
+ mDisplayWhiteBalanceController.setEnabled(false);
+ }
+ }
+
// Determine whether the display is ready for use in the newly requested state.
// Note that we do not wait for the brightness ramp animation to complete before
// reporting the display is ready because we only need to ensure the screen is in the
@@ -1702,6 +1739,12 @@
pw.println();
mBrightnessTracker.dump(pw);
}
+
+ pw.println();
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.dump(pw);
+ mDisplayWhiteBalanceSettings.dump(pw);
+ }
}
private static String proximityToString(int state) {
@@ -1848,4 +1891,26 @@
mAutomaticBrightnessController.setLoggingEnabled(enabled);
}
}
+
+ @Override // DisplayWhiteBalanceController.Callbacks
+ public void updateWhiteBalance() {
+ sendUpdatePowerState();
+ }
+
+ void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
+ mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
+ }
+ }
+
+ void setAmbientColorTemperatureOverride(float cct) {
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
+ // The ambient color temperature override is only applied when the ambient color
+ // temperature changes or is updated, so it doesn't necessarily change the screen color
+ // temperature immediately. So, let's make it!
+ sendUpdatePowerState();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/utils/History.java b/services/core/java/com/android/server/display/utils/History.java
new file mode 100644
index 0000000..ed171b8
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/History.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 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.display.utils;
+
+import java.time.Clock;
+
+/**
+ * A fixed-size buffer that keeps the most recent values and their times.
+ *
+ * This class is used for logging and debugging purposes only, so there's no way to retrieve the
+ * history other than toString(), and a non-monotonic clock is good enough.
+ */
+public class History {
+
+ private int mSize;
+ private int mCount;
+ private int mStart;
+ private int mEnd;
+
+ private long[] mTimes;
+ private float[] mValues;
+
+ private Clock mClock;
+
+ /**
+ * @param size
+ * The maximum number of values kept.
+ */
+ public History(int size) {
+ this(size, Clock.systemUTC());
+ }
+
+ /**
+ * @param size
+ * The maximum number of values kept.
+ * @param clock
+ * The clock used.
+ */
+ public History(int size, Clock clock) {
+ mSize = size;
+ mCount = 0;
+ mStart = 0;
+ mEnd = 0;
+ mTimes = new long[size];
+ mValues = new float[size];
+ mClock = clock;
+ }
+
+ /**
+ * Add a value.
+ *
+ * @param value
+ * The value.
+ */
+ public void add(float value) {
+ mTimes[mEnd] = mClock.millis();
+ mValues[mEnd] = value;
+ if (mCount < mSize) {
+ mCount++;
+ } else {
+ mStart = (mStart + 1) % mSize;
+ }
+ mEnd = (mEnd + 1) % mSize;
+ }
+
+ /**
+ * Convert the buffer to string.
+ *
+ * @return The buffer as string.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[");
+ for (int i = 0; i < mCount; i++) {
+ final int index = (mStart + i) % mSize;
+ final long time = mTimes[index];
+ final float value = mValues[index];
+ sb.append(value + " @ " + time);
+ if (i + 1 != mCount) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/utils/RollingBuffer.java b/services/core/java/com/android/server/display/utils/RollingBuffer.java
new file mode 100644
index 0000000..dd5b7ab
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/RollingBuffer.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 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.display.utils;
+
+/**
+ * A buffer that supports adding new values and truncating old ones.
+ */
+public class RollingBuffer {
+
+ private static final int INITIAL_SIZE = 50;
+
+ private int mSize;
+ private int mCount;
+ private int mStart;
+ private int mEnd;
+
+ private long[] mTimes; // Milliseconds
+ private float[] mValues;
+
+ public RollingBuffer() {
+ mSize = INITIAL_SIZE;
+ mTimes = new long[INITIAL_SIZE];
+ mValues = new float[INITIAL_SIZE];
+ clear();
+ }
+
+ /**
+ * Add a value at a given time.
+ *
+ * @param time
+ * The time (in milliseconds).
+ * @param value
+ * The value.
+ */
+ public void add(long time, float value) {
+ if (mCount >= mSize) {
+ expandBuffer();
+ }
+ mTimes[mEnd] = time;
+ mValues[mEnd] = value;
+ mEnd = (mEnd + 1) % mSize;
+ mCount++;
+ }
+
+ /**
+ * Get the size of the buffer.
+ *
+ * @return The size of the buffer.
+ */
+ public int size() {
+ return mCount;
+ }
+
+ /**
+ * Return whether the buffer is empty or not.
+ *
+ * @return Whether the buffer is empty or not.
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * Get a time.
+ *
+ * @param index
+ * The index of the time.
+ *
+ * @return The time.
+ */
+ public long getTime(int index) {
+ return mTimes[offsetOf(index)];
+ }
+
+ /**
+ * Get a value.
+ *
+ * @param index
+ * The index of the value.
+ *
+ * @return The value.
+ */
+ public float getValue(int index) {
+ return mValues[offsetOf(index)];
+ }
+
+ /**
+ * Truncate old values.
+ *
+ * @param minTime
+ * The minimum time (all values older than this time are truncated).
+ */
+ public void truncate(long minTime) {
+ if (isEmpty() || getTime(0) >= minTime) {
+ return;
+ }
+ final int index = getLatestIndexBefore(minTime);
+ mStart = offsetOf(index);
+ mCount -= index;
+ // Remove everything that happened before mTimes[index], but set the index-th value time to
+ // minTime rather than dropping it, as that would've been the value between the minTime and
+ // mTimes[index+1].
+ //
+ // -*---*---|---*---*- => xxxxxxxxx|*--*---*- rather than xxxxxxxxx|???*---*-
+ // ^ ^ ^ ^ ^
+ // i i+1 i i+1 i+1
+ mTimes[mStart] = minTime;
+ }
+
+ /**
+ * Clears the buffer.
+ */
+ public void clear() {
+ mCount = 0;
+ mStart = 0;
+ mEnd = 0;
+ }
+
+ /**
+ * Convert the buffer to string.
+ *
+ * @return The buffer as string.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[");
+ for (int i = 0; i < mCount; i++) {
+ final int index = offsetOf(i);
+ sb.append(mValues[index] + " @ " + mTimes[index]);
+ if (i + 1 != mCount) {
+ sb.append(", ");
+ }
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ private int offsetOf(int index) {
+ if (index < 0 || index >= mCount) {
+ throw new ArrayIndexOutOfBoundsException("invalid index: " + index + ", mCount= "
+ + mCount);
+ }
+ return (mStart + index) % mSize;
+ }
+
+ private void expandBuffer() {
+ final int size = mSize * 2;
+ long[] times = new long[size];
+ float[] values = new float[size];
+ System.arraycopy(mTimes, mStart, times, 0, mCount - mStart);
+ System.arraycopy(mTimes, 0, times, mCount - mStart, mStart);
+ System.arraycopy(mValues, mStart, values, 0, mCount - mStart);
+ System.arraycopy(mValues, 0, values, mCount - mStart, mStart);
+ mSize = size;
+ mStart = 0;
+ mEnd = mCount;
+ mTimes = times;
+ mValues = values;
+ }
+
+ private int getLatestIndexBefore(long time) {
+ for (int i = 1; i < mCount; i++) {
+ if (mTimes[offsetOf(i)] > time) {
+ return i - 1;
+ }
+ }
+ return mCount - 1;
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
new file mode 100644
index 0000000..532bbed
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2019 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.display.whitebalance;
+
+import android.util.Slog;
+
+import com.android.server.display.utils.RollingBuffer;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * The DisplayWhiteBalanceController uses the AmbientFilter to average ambient changes over time,
+ * filter out the noise, and arrive at an estimate of the actual value.
+ *
+ * When the DisplayWhiteBalanceController detects a change in ambient brightness or color
+ * temperature, it passes it to the AmbientFilter, and when it needs the actual ambient value, it
+ * asks it for an estimate.
+ *
+ * Implementations:
+ * - {@link WeightedMovingAverageAmbientFilter}
+ * A weighted average prioritising recent changes.
+ */
+abstract class AmbientFilter {
+
+ protected static final boolean DEBUG = false; // Enable for verbose logs.
+
+ protected final String mTag;
+ protected boolean mLoggingEnabled;
+
+ // How long ambient value changes are kept and taken into consideration.
+ private final int mHorizon; // Milliseconds
+
+ private final RollingBuffer mBuffer;
+
+ /**
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param horizon
+ * How long ambient value changes are kept and taken into consideration.
+ *
+ * @throws IllegalArgumentException
+ * - horizon is not positive.
+ */
+ AmbientFilter(String tag, int horizon) {
+ validateArguments(horizon);
+ mTag = tag;
+ mLoggingEnabled = false;
+ mHorizon = horizon;
+ mBuffer = new RollingBuffer();
+ }
+
+ /**
+ * Add an ambient value change.
+ *
+ * @param time
+ * The time.
+ * @param value
+ * The ambient value.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean addValue(long time, float value) {
+ if (value < 0.0f) {
+ return false;
+ }
+ truncateOldValues(time);
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "add value: " + value + " @ " + time);
+ }
+ mBuffer.add(time, value);
+ return true;
+ }
+
+ /**
+ * Get an estimate of the actual ambient color temperature.
+ *
+ * @param time
+ * The time.
+ *
+ * @return An estimate of the actual ambient color temperature.
+ */
+ public float getEstimate(long time) {
+ truncateOldValues(time);
+ final float value = filter(time, mBuffer);
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "get estimate: " + value + " @ " + time);
+ }
+ return value;
+ }
+
+ /**
+ * Clears the filter state.
+ */
+ public void clear() {
+ mBuffer.clear();
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging is on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Dump the state.
+ *
+ * @param writer
+ * The PrintWriter used to dump the state.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println(" " + mTag);
+ writer.println(" mLoggingEnabled=" + mLoggingEnabled);
+ writer.println(" mHorizon=" + mHorizon);
+ writer.println(" mBuffer=" + mBuffer);
+ }
+
+ private void validateArguments(int horizon) {
+ if (horizon <= 0) {
+ throw new IllegalArgumentException("horizon must be positive");
+ }
+ }
+
+ private void truncateOldValues(long time) {
+ final long minTime = time - mHorizon;
+ mBuffer.truncate(minTime);
+ }
+
+ protected abstract float filter(long time, RollingBuffer buffer);
+
+ /**
+ * A weighted average prioritising recent changes.
+ */
+ static class WeightedMovingAverageAmbientFilter extends AmbientFilter {
+
+ // How long the latest ambient value change is predicted to last.
+ private static final int PREDICTION_TIME = 100; // Milliseconds
+
+ // Recent changes are prioritised by integrating their duration over y = x + mIntercept
+ // (the higher it is, the less prioritised recent changes are).
+ private final float mIntercept;
+
+ /**
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param horizon
+ * How long ambient value changes are kept and taken into consideration.
+ * @param intercept
+ * Recent changes are prioritised by integrating their duration over y = x + intercept
+ * (the higher it is, the less prioritised recent changes are).
+ *
+ * @throws IllegalArgumentException
+ * - horizon is not positive.
+ * - intercept is NaN or negative.
+ */
+ WeightedMovingAverageAmbientFilter(String tag, int horizon, float intercept) {
+ super(tag, horizon);
+ validateArguments(intercept);
+ mIntercept = intercept;
+ }
+
+ /**
+ * See {@link AmbientFilter#dump base class}.
+ */
+ @Override
+ public void dump(PrintWriter writer) {
+ super.dump(writer);
+ writer.println(" mIntercept=" + mIntercept);
+ }
+
+ // Normalise the times to [t1=0, t2, ..., tN, now + PREDICTION_TIME], so the first change
+ // starts at 0 and the last change is predicted to last a bit, and divide them by 1000 as
+ // milliseconds are high enough to overflow.
+ // The weight of the value from t[i] to t[i+1] is the area under (A.K.A. the integral of)
+ // y = x + mIntercept from t[i] to t[i+1].
+ @Override
+ protected float filter(long time, RollingBuffer buffer) {
+ if (buffer.isEmpty()) {
+ return -1.0f;
+ }
+ float total = 0.0f;
+ float totalWeight = 0.0f;
+ final float[] weights = getWeights(time, buffer);
+ if (DEBUG && mLoggingEnabled) {
+ Slog.v(mTag, "filter: " + buffer + " => " + Arrays.toString(weights));
+ }
+ for (int i = 0; i < weights.length; i++) {
+ final float value = buffer.getValue(i);
+ final float weight = weights[i];
+ total += weight * value;
+ totalWeight += weight;
+ }
+ if (totalWeight == 0.0f) {
+ return buffer.getValue(buffer.size() - 1);
+ }
+ return total / totalWeight;
+ }
+
+ private void validateArguments(float intercept) {
+ if (Float.isNaN(intercept) || intercept < 0.0f) {
+ throw new IllegalArgumentException("intercept must be a non-negative number");
+ }
+ }
+
+ private float[] getWeights(long time, RollingBuffer buffer) {
+ float[] weights = new float[buffer.size()];
+ final long startTime = buffer.getTime(0);
+ float previousTime = 0.0f;
+ for (int i = 1; i < weights.length; i++) {
+ final float currentTime = (buffer.getTime(i) - startTime) / 1000.0f;
+ final float weight = calculateIntegral(previousTime, currentTime);
+ weights[i - 1] = weight;
+ previousTime = currentTime;
+ }
+ final float lastTime = (time + PREDICTION_TIME - startTime) / 1000.0f;
+ final float lastWeight = calculateIntegral(previousTime, lastTime);
+ weights[weights.length - 1] = lastWeight;
+ return weights;
+ }
+
+ private float calculateIntegral(float from, float to) {
+ return antiderivative(to) - antiderivative(from);
+ }
+
+ private float antiderivative(float x) {
+ // f(x) = x + c => F(x) = 1/2 * x^2 + c * x
+ return 0.5f * x * x + mIntercept * x;
+ }
+
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java b/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
new file mode 100644
index 0000000..1707a62
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2019 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.display.whitebalance;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.display.utils.History;
+
+import java.io.PrintWriter;
+
+/**
+ * The DisplayWhiteBalanceController uses the AmbientSensor to detect changes in the ambient
+ * brightness and color temperature.
+ *
+ * The AmbientSensor listens on an actual sensor, derives the ambient brightness or color
+ * temperature from its events, and calls back into the DisplayWhiteBalanceController to report it.
+ */
+abstract class AmbientSensor {
+
+ protected String mTag;
+ protected boolean mLoggingEnabled;
+
+ private final Handler mHandler;
+
+ protected final SensorManager mSensorManager;
+ protected Sensor mSensor;
+
+ private boolean mEnabled;
+
+ private int mRate; // Milliseconds
+
+ // The total events count and the most recent events are kept for debugging purposes.
+ private int mEventsCount;
+ private static final int HISTORY_SIZE = 50;
+ private History mEventsHistory;
+
+ /**
+ * @param tag
+ * The tag used for dumping and logging.
+ * @param handler
+ * The handler used to determine which thread to run on.
+ * @param sensorManager
+ * The sensor manager used to acquire necessary sensors.
+ * @param rate
+ * The sensor rate.
+ *
+ * @throws IllegalArgumentException
+ * - rate is not positive.
+ * @throws NullPointerException
+ * - handler is null;
+ * - sensorManager is null.
+ * @throws IllegalStateException
+ * - Cannot find the necessary sensor.
+ */
+ AmbientSensor(String tag, @NonNull Handler handler, @NonNull SensorManager sensorManager,
+ int rate) {
+ validateArguments(handler, sensorManager, rate);
+ mTag = tag;
+ mLoggingEnabled = false;
+ mHandler = handler;
+ mSensorManager = sensorManager;
+ mEnabled = false;
+ mRate = rate;
+ mEventsCount = 0;
+ mEventsHistory = new History(HISTORY_SIZE);
+ }
+
+ /**
+ * Enable/disable the sensor.
+ *
+ * @param enabled
+ * Whether the sensor should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setEnabled(boolean enabled) {
+ if (enabled) {
+ return enable();
+ } else {
+ return disable();
+ }
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Dump the state.
+ *
+ * @param writer
+ * The PrintWriter used to dump the state.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println(" " + mTag);
+ writer.println(" mLoggingEnabled=" + mLoggingEnabled);
+ writer.println(" mHandler=" + mHandler);
+ writer.println(" mSensorManager=" + mSensorManager);
+ writer.println(" mSensor=" + mSensor);
+ writer.println(" mEnabled=" + mEnabled);
+ writer.println(" mRate=" + mRate);
+ writer.println(" mEventsCount=" + mEventsCount);
+ writer.println(" mEventsHistory=" + mEventsHistory);
+ }
+
+
+ private static void validateArguments(Handler handler, SensorManager sensorManager, int rate) {
+ Preconditions.checkNotNull(handler, "handler cannot be null");
+ Preconditions.checkNotNull(sensorManager, "sensorManager cannot be null");
+ if (rate <= 0) {
+ throw new IllegalArgumentException("rate must be positive");
+ }
+ }
+
+ protected abstract void update(float value);
+
+ private boolean enable() {
+ if (mEnabled) {
+ return false;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "enabling");
+ }
+ mEnabled = true;
+ startListening();
+ return true;
+ }
+
+ private boolean disable() {
+ if (!mEnabled) {
+ return false;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "disabling");
+ }
+ mEnabled = false;
+ mEventsCount = 0;
+ stopListening();
+ return true;
+ }
+
+ private void startListening() {
+ if (mSensorManager == null) {
+ return;
+ }
+ mSensorManager.registerListener(mListener, mSensor, mRate * 1000, mHandler);
+ }
+
+ private void stopListening() {
+ if (mSensorManager == null) {
+ return;
+ }
+ mSensorManager.unregisterListener(mListener);
+ }
+
+ private void handleNewEvent(float value) {
+ // This shouldn't really happen, except for the race condition where the sensor is disabled
+ // with an event already in the handler queue, in which case we discard that event.
+ if (!mEnabled) {
+ return;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(mTag, "handle new event: " + value);
+ }
+ mEventsCount++;
+ mEventsHistory.add(value);
+ update(value);
+ }
+
+ private SensorEventListener mListener = new SensorEventListener() {
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ final float value = event.values[0];
+ handleNewEvent(value);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+
+ };
+
+ /**
+ * A sensor that reports the ambient brightness.
+ */
+ static class AmbientBrightnessSensor extends AmbientSensor {
+
+ private static final String TAG = "AmbientBrightnessSensor";
+
+ // To decouple the DisplayWhiteBalanceController from the AmbientBrightnessSensor, the
+ // DWBC implements Callbacks and passes itself to the ABS so it can call back into it
+ // without knowing about it.
+ @Nullable
+ private Callbacks mCallbacks;
+
+ /**
+ * @param handler
+ * The handler used to determine which thread to run on.
+ * @param sensorManager
+ * The sensor manager used to acquire necessary sensors.
+ * @param rate
+ * The sensor rate.
+ *
+ * @throws IllegalArgumentException
+ * - rate is not positive.
+ * @throws NullPointerException
+ * - handler is null;
+ * - sensorManager is null.
+ * @throws IllegalStateException
+ * - Cannot find the light sensor.
+ */
+ AmbientBrightnessSensor(@NonNull Handler handler, @NonNull SensorManager sensorManager,
+ int rate) {
+ super(TAG, handler, sensorManager, rate);
+ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ if (mSensor == null) {
+ throw new IllegalStateException("cannot find light sensor");
+ }
+ mCallbacks = null;
+ }
+
+ /**
+ * Set an object to call back to when the ambient brightness changes.
+ *
+ * @param callbacks
+ * The object to call back to.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setCallbacks(Callbacks callbacks) {
+ if (mCallbacks == callbacks) {
+ return false;
+ }
+ mCallbacks = callbacks;
+ return true;
+ }
+
+ /**
+ * See {@link AmbientSensor#dump base class}.
+ */
+ @Override
+ public void dump(PrintWriter writer) {
+ super.dump(writer);
+ writer.println(" mCallbacks=" + mCallbacks);
+ }
+
+ interface Callbacks {
+ void onAmbientBrightnessChanged(float value);
+ }
+
+ @Override
+ protected void update(float value) {
+ if (mCallbacks != null) {
+ mCallbacks.onAmbientBrightnessChanged(value);
+ }
+ }
+
+ }
+
+ /**
+ * A sensor that reports the ambient color temperature.
+ */
+ static class AmbientColorTemperatureSensor extends AmbientSensor {
+
+ private static final String TAG = "AmbientColorTemperatureSensor";
+
+ // To decouple the DisplayWhiteBalanceController from the
+ // AmbientColorTemperatureSensor, the DWBC implements Callbacks and passes itself to the
+ // ACTS so it can call back into it without knowing about it.
+ @Nullable
+ private Callbacks mCallbacks;
+
+ /**
+ * @param handler
+ * The handler used to determine which thread to run on.
+ * @param sensorManager
+ * The sensor manager used to acquire necessary sensors.
+ * @param name
+ * The color sensor name.
+ * @param rate
+ * The sensor rate.
+ *
+ * @throws IllegalArgumentException
+ * - rate is not positive.
+ * @throws NullPointerException
+ * - handler is null;
+ * - sensorManager is null.
+ * @throws IllegalStateException
+ * - Cannot find the color sensor.
+ */
+ AmbientColorTemperatureSensor(@NonNull Handler handler,
+ @NonNull SensorManager sensorManager, String name, int rate) {
+ super(TAG, handler, sensorManager, rate);
+ mSensor = null;
+ for (Sensor sensor : mSensorManager.getSensorList(Sensor.TYPE_ALL)) {
+ if (sensor.getStringType().equals(name)) {
+ mSensor = sensor;
+ break;
+ }
+ }
+ if (mSensor == null) {
+ throw new IllegalStateException("cannot find sensor " + name);
+ }
+ mCallbacks = null;
+ }
+
+ /**
+ * Set an object to call back to when the ambient color temperature changes.
+ *
+ * @param callbacks
+ * The object to call back to.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setCallbacks(Callbacks callbacks) {
+ if (mCallbacks == callbacks) {
+ return false;
+ }
+ mCallbacks = callbacks;
+ return true;
+ }
+
+ /**
+ * See {@link AmbientSensor#dump base class}.
+ */
+ @Override
+ public void dump(PrintWriter writer) {
+ super.dump(writer);
+ writer.println(" mCallbacks=" + mCallbacks);
+ }
+
+ interface Callbacks {
+ void onAmbientColorTemperatureChanged(float value);
+ }
+
+ @Override
+ protected void update(float value) {
+ if (mCallbacks != null) {
+ mCallbacks.onAmbientColorTemperatureChanged(value);
+ }
+ }
+
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
new file mode 100644
index 0000000..7ae00af
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2019 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.display.whitebalance;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.utils.History;
+
+import java.io.PrintWriter;
+
+/**
+ * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
+ * screen color temperature depending on the ambient color temperature).
+ *
+ * The DisplayWhiteBalanceController:
+ * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
+ * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
+ * noise, and arrive at an estimate of the actual ambient color temperature;
+ * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be
+ * updated, suppressing changes that are too frequent or too minor.
+ */
+public class DisplayWhiteBalanceController implements
+ AmbientSensor.AmbientBrightnessSensor.Callbacks,
+ AmbientSensor.AmbientColorTemperatureSensor.Callbacks {
+
+ protected static final String TAG = "DisplayWhiteBalanceController";
+ protected boolean mLoggingEnabled;
+
+ private boolean mEnabled;
+
+ // To decouple the DisplayPowerController from the DisplayWhiteBalanceController, the DPC
+ // implements Callbacks and passes itself to the DWBC so it can call back into it without
+ // knowing about it.
+ private Callbacks mCallbacks;
+
+ private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor;
+ private AmbientFilter mBrightnessFilter;
+ private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor;
+ private AmbientFilter mColorTemperatureFilter;
+ private DisplayWhiteBalanceThrottler mThrottler;
+
+ // When the brightness drops below a certain threshold, it affects the color temperature
+ // accuracy, so we fall back to a fixed ambient color temperature.
+ private final float mLowLightAmbientBrightnessThreshold;
+ private final float mLowLightAmbientColorTemperature;
+
+ private float mAmbientColorTemperature;
+ private float mPendingAmbientColorTemperature;
+ private float mLastAmbientColorTemperature;
+
+ private ColorDisplayServiceInternal mColorDisplayServiceInternal;
+
+ // The most recent ambient color temperature values are kept for debugging purposes.
+ private static final int HISTORY_SIZE = 50;
+ private History mAmbientColorTemperatureHistory;
+
+ // Override the ambient color temperature for debugging purposes.
+ private float mAmbientColorTemperatureOverride;
+
+ /**
+ * @param brightnessSensor
+ * The sensor used to detect changes in the ambient brightness.
+ * @param brightnessFilter
+ * The filter used to avergae ambient brightness changes over time, filter out the noise
+ * and arrive at an estimate of the actual ambient brightness.
+ * @param colorTemperatureSensor
+ * The sensor used to detect changes in the ambient color temperature.
+ * @param colorTemperatureFilter
+ * The filter used to average ambient color temperature changes over time, filter out the
+ * noise and arrive at an estimate of the actual ambient color temperature.
+ * @param throttler
+ * The throttler used to determine whether the new screen color temperature should be
+ * updated or not.
+ * @param lowLightAmbientBrightnessThreshold
+ * The ambient brightness threshold beneath which we fall back to a fixed ambient color
+ * temperature.
+ * @param lowLightAmbientColorTemperature
+ * The ambient color temperature to which we fall back when the ambient brightness drops
+ * beneath a certain threshold.
+ *
+ * @throws NullPointerException
+ * - brightnessSensor is null;
+ * - brightnessFilter is null;
+ * - colorTemperatureSensor is null;
+ * - colorTemperatureFilter is null;
+ * - throttler is null.
+ */
+ public DisplayWhiteBalanceController(
+ @NonNull AmbientSensor.AmbientBrightnessSensor brightnessSensor,
+ @NonNull AmbientFilter brightnessFilter,
+ @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
+ @NonNull AmbientFilter colorTemperatureFilter,
+ @NonNull DisplayWhiteBalanceThrottler throttler,
+ float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature) {
+ validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
+ colorTemperatureFilter, throttler);
+ mLoggingEnabled = false;
+ mEnabled = false;
+ mCallbacks = null;
+ mBrightnessSensor = brightnessSensor;
+ mBrightnessFilter = brightnessFilter;
+ mColorTemperatureSensor = colorTemperatureSensor;
+ mColorTemperatureFilter = colorTemperatureFilter;
+ mThrottler = throttler;
+ mLowLightAmbientBrightnessThreshold = lowLightAmbientBrightnessThreshold;
+ mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature;
+ mAmbientColorTemperature = -1.0f;
+ mPendingAmbientColorTemperature = -1.0f;
+ mLastAmbientColorTemperature = -1.0f;
+ mAmbientColorTemperatureHistory = new History(HISTORY_SIZE);
+ mAmbientColorTemperatureOverride = -1.0f;
+ mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
+ }
+
+ /**
+ * Enable/disable the controller.
+ *
+ * @param enabled
+ * Whether the controller should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setEnabled(boolean enabled) {
+ if (enabled) {
+ return enable();
+ } else {
+ return disable();
+ }
+ }
+
+ /**
+ * Set an object to call back to when the screen color temperature should be updated.
+ *
+ * @param callbacks
+ * The object to call back to.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setCallbacks(Callbacks callbacks) {
+ if (mCallbacks == callbacks) {
+ return false;
+ }
+ mCallbacks = callbacks;
+ return true;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ mBrightnessSensor.setLoggingEnabled(loggingEnabled);
+ mBrightnessFilter.setLoggingEnabled(loggingEnabled);
+ mColorTemperatureSensor.setLoggingEnabled(loggingEnabled);
+ mColorTemperatureFilter.setLoggingEnabled(loggingEnabled);
+ mThrottler.setLoggingEnabled(loggingEnabled);
+ return true;
+ }
+
+ /**
+ * Set the ambient color temperature override.
+ *
+ * This is only applied when the ambient color temperature changes or is updated (in which case
+ * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
+ * change the screen color temperature immediately.
+ *
+ * @param ambientColorTemperatureOverride
+ * The ambient color temperature override.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setAmbientColorTemperatureOverride(float ambientColorTemperatureOverride) {
+ if (mAmbientColorTemperatureOverride == ambientColorTemperatureOverride) {
+ return false;
+ }
+ mAmbientColorTemperatureOverride = ambientColorTemperatureOverride;
+ return true;
+ }
+
+ /**
+ * Dump the state.
+ *
+ * @param writer
+ * The writer used to dump the state.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println("DisplayWhiteBalanceController");
+ writer.println(" mLoggingEnabled=" + mLoggingEnabled);
+ writer.println(" mEnabled=" + mEnabled);
+ writer.println(" mCallbacks=" + mCallbacks);
+ mBrightnessSensor.dump(writer);
+ mBrightnessFilter.dump(writer);
+ mColorTemperatureSensor.dump(writer);
+ mColorTemperatureFilter.dump(writer);
+ mThrottler.dump(writer);
+ writer.println(" mLowLightAmbientBrightnessThreshold="
+ + mLowLightAmbientBrightnessThreshold);
+ writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature);
+ writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature);
+ writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature);
+ writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
+ writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
+ writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
+ }
+
+ @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
+ public void onAmbientBrightnessChanged(float value) {
+ final long time = System.currentTimeMillis();
+ mBrightnessFilter.addValue(time, value);
+ updateAmbientColorTemperature();
+ }
+
+ @Override // AmbientSensor.AmbientColorTemperatureSensor.Callbacks
+ public void onAmbientColorTemperatureChanged(float value) {
+ final long time = System.currentTimeMillis();
+ mColorTemperatureFilter.addValue(time, value);
+ updateAmbientColorTemperature();
+ }
+
+ /**
+ * Updates the ambient color temperature.
+ */
+ public void updateAmbientColorTemperature() {
+ final long time = System.currentTimeMillis();
+ float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
+
+ final float ambientBrightness = mBrightnessFilter.getEstimate(time);
+ if (ambientBrightness < mLowLightAmbientBrightnessThreshold) {
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "low light ambient brightness: " + ambientBrightness + " < "
+ + mLowLightAmbientBrightnessThreshold
+ + ", falling back to fixed ambient color temperature: "
+ + ambientColorTemperature + " => " + mLowLightAmbientColorTemperature);
+ }
+ ambientColorTemperature = mLowLightAmbientColorTemperature;
+ }
+
+ if (mAmbientColorTemperatureOverride != -1.0f) {
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "override ambient color temperature: " + ambientColorTemperature
+ + " => " + mAmbientColorTemperatureOverride);
+ }
+ ambientColorTemperature = mAmbientColorTemperatureOverride;
+ }
+
+ // When the screen color temperature needs to be updated, we call DisplayPowerController to
+ // call our updateColorTemperature. The reason we don't call it directly is that we want
+ // all changes to the system to happen in a predictable order in DPC's main loop
+ // (updatePowerState).
+ if (ambientColorTemperature == -1.0f || mThrottler.throttle(ambientColorTemperature)) {
+ return;
+ }
+
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "pending ambient color temperature: " + ambientColorTemperature);
+ }
+ mPendingAmbientColorTemperature = ambientColorTemperature;
+ if (mCallbacks != null) {
+ mCallbacks.updateWhiteBalance();
+ }
+ }
+
+ /**
+ * Updates the screen color temperature.
+ */
+ public void updateScreenColorTemperature() {
+ float ambientColorTemperature = -1.0f;
+
+ // If both the pending and the current ambient color temperatures are -1, it means the DWBC
+ // was just enabled, and we use the last ambient color temperature until new sensor events
+ // give us a better estimate.
+ if (mAmbientColorTemperature == -1.0f && mPendingAmbientColorTemperature == -1.0f) {
+ ambientColorTemperature = mLastAmbientColorTemperature;
+ }
+
+ // Otherwise, we use the pending ambient color temperature, but only if it's non-trivial
+ // and different than the current one.
+ if (mPendingAmbientColorTemperature != -1.0f
+ && mPendingAmbientColorTemperature != mAmbientColorTemperature) {
+ ambientColorTemperature = mPendingAmbientColorTemperature;
+ }
+
+ if (ambientColorTemperature == -1.0f) {
+ return;
+ }
+
+ mAmbientColorTemperature = ambientColorTemperature;
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "ambient color temperature: " + mAmbientColorTemperature);
+ }
+ mPendingAmbientColorTemperature = -1.0f;
+ mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
+ mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
+ (int) mAmbientColorTemperature);
+ mLastAmbientColorTemperature = mAmbientColorTemperature;
+ }
+
+ /**
+ * The DisplayWhiteBalanceController decouples itself from its parent (DisplayPowerController)
+ * by providing this interface to implement (and a method to set its callbacks object), and
+ * calling these methods.
+ */
+ public interface Callbacks {
+
+ /**
+ * Called whenever the display white-balance state has changed.
+ *
+ * Usually, this means the estimated ambient color temperature has changed enough, and the
+ * screen color temperature should be updated; but it is also called by
+ */
+ void updateWhiteBalance();
+ }
+
+ private void validateArguments(AmbientSensor.AmbientBrightnessSensor brightnessSensor,
+ AmbientFilter brightnessFilter,
+ AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
+ AmbientFilter colorTemperatureFilter,
+ DisplayWhiteBalanceThrottler throttler) {
+ Preconditions.checkNotNull(brightnessSensor, "brightnessSensor must not be null");
+ Preconditions.checkNotNull(brightnessFilter, "brightnessFilter must not be null");
+ Preconditions.checkNotNull(colorTemperatureSensor,
+ "colorTemperatureSensor must not be null");
+ Preconditions.checkNotNull(colorTemperatureFilter,
+ "colorTemperatureFilter must not be null");
+ Preconditions.checkNotNull(throttler, "throttler cannot be null");
+ }
+
+ private boolean enable() {
+ if (mEnabled) {
+ return false;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "enabling");
+ }
+ mEnabled = true;
+ mBrightnessSensor.setEnabled(true);
+ mColorTemperatureSensor.setEnabled(true);
+ return true;
+ }
+
+ private boolean disable() {
+ if (!mEnabled) {
+ return false;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "disabling");
+ }
+ mEnabled = false;
+ mBrightnessSensor.setEnabled(false);
+ mBrightnessFilter.clear();
+ mColorTemperatureSensor.setEnabled(false);
+ mColorTemperatureFilter.clear();
+ mThrottler.clear();
+ mAmbientColorTemperature = -1.0f;
+ mPendingAmbientColorTemperature = -1.0f;
+ return true;
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
new file mode 100644
index 0000000..fd78ddb
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 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.display.whitebalance;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.TypedValue;
+
+/**
+ * The DisplayWhiteBalanceFactory creates and configures an DisplayWhiteBalanceController.
+ */
+public class DisplayWhiteBalanceFactory {
+
+ private static final String BRIGHTNESS_FILTER_TAG = "AmbientBrightnessFilter";
+ private static final String COLOR_TEMPERATURE_FILTER_TAG = "AmbientColorTemperatureFilter";
+
+ /**
+ * Create and configure an DisplayWhiteBalanceController.
+ *
+ * @param handler
+ * The handler used to determine which thread to run on.
+ * @param sensorManager
+ * The sensor manager used to acquire necessary sensors.
+ * @param resources
+ * The resources used to configure the various components.
+ *
+ * @return An DisplayWhiteBalanceController.
+ *
+ * @throws NullPointerException
+ * - handler is null;
+ * - sensorManager is null.
+ * @throws Resources.NotFoundException
+ * - Configurations are missing.
+ * @throws IllegalArgumentException
+ * - Configurations are invalid.
+ * @throws IllegalStateException
+ * - Cannot find the necessary sensors.
+ */
+ public static DisplayWhiteBalanceController create(Handler handler,
+ SensorManager sensorManager, Resources resources) {
+ final AmbientSensor.AmbientBrightnessSensor brightnessSensor =
+ createBrightnessSensor(handler, sensorManager, resources);
+ final AmbientFilter brightnessFilter = createBrightnessFilter(resources);
+ final AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor =
+ createColorTemperatureSensor(handler, sensorManager, resources);
+ final AmbientFilter colorTemperatureFilter = createColorTemperatureFilter(resources);
+ final DisplayWhiteBalanceThrottler throttler = createThrottler(resources);
+ final float lowLightAmbientBrightnessThreshold = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceLowLightAmbientBrightnessThreshold);
+ final float lowLightAmbientColorTemperature = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceLowLightAmbientColorTemperature);
+ final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
+ brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
+ throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature);
+ brightnessSensor.setCallbacks(controller);
+ colorTemperatureSensor.setCallbacks(controller);
+ return controller;
+ }
+
+ // Instantiation is disabled.
+ private DisplayWhiteBalanceFactory() { }
+
+ private static AmbientSensor.AmbientBrightnessSensor createBrightnessSensor(Handler handler,
+ SensorManager sensorManager, Resources resources) {
+ final int rate = resources.getInteger(
+ com.android.internal.R.integer.config_displayWhiteBalanceBrightnessSensorRate);
+ return new AmbientSensor.AmbientBrightnessSensor(handler, sensorManager, rate);
+ }
+
+ private static AmbientFilter createBrightnessFilter(Resources resources) {
+ final int horizon = resources.getInteger(
+ com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon);
+ final float intercept = getFloat(resources,
+ com.android.internal.R.dimen.config_displayWhiteBalanceBrightnessFilterIntercept);
+ if (!Float.isNaN(intercept)) {
+ return new AmbientFilter.WeightedMovingAverageAmbientFilter(
+ BRIGHTNESS_FILTER_TAG, horizon, intercept);
+ }
+ throw new IllegalArgumentException("missing configurations: "
+ + "expected config_displayWhiteBalanceBrightnessFilterIntercept");
+ }
+
+
+ private static AmbientSensor.AmbientColorTemperatureSensor createColorTemperatureSensor(
+ Handler handler, SensorManager sensorManager, Resources resources) {
+ final String name = resources.getString(
+ com.android.internal.R.string
+ .config_displayWhiteBalanceColorTemperatureSensorName);
+ final int rate = resources.getInteger(
+ com.android.internal.R.integer
+ .config_displayWhiteBalanceColorTemperatureSensorRate);
+ return new AmbientSensor.AmbientColorTemperatureSensor(handler, sensorManager, name, rate);
+ }
+
+ private static AmbientFilter createColorTemperatureFilter(Resources resources) {
+ final int horizon = resources.getInteger(
+ com.android.internal.R.integer
+ .config_displayWhiteBalanceColorTemperatureFilterHorizon);
+ final float intercept = getFloat(resources,
+ com.android.internal.R.dimen
+ .config_displayWhiteBalanceColorTemperatureFilterIntercept);
+ if (!Float.isNaN(intercept)) {
+ return new AmbientFilter.WeightedMovingAverageAmbientFilter(
+ COLOR_TEMPERATURE_FILTER_TAG, horizon, intercept);
+ }
+ throw new IllegalArgumentException("missing configurations: "
+ + "expected config_displayWhiteBalanceColorTemperatureFilterIntercept");
+ }
+
+ private static DisplayWhiteBalanceThrottler createThrottler(Resources resources) {
+ final int increaseDebounce = resources.getInteger(
+ com.android.internal.R.integer.config_displayWhiteBalanceDecreaseDebounce);
+ final int decreaseDebounce = resources.getInteger(
+ com.android.internal.R.integer.config_displayWhiteBalanceIncreaseDebounce);
+ final float[] baseThresholds = getFloatArray(resources,
+ com.android.internal.R.array.config_displayWhiteBalanceBaseThresholds);
+ final float[] increaseThresholds = getFloatArray(resources,
+ com.android.internal.R.array.config_displayWhiteBalanceIncreaseThresholds);
+ final float[] decreaseThresholds = getFloatArray(resources,
+ com.android.internal.R.array.config_displayWhiteBalanceDecreaseThresholds);
+ return new DisplayWhiteBalanceThrottler(increaseDebounce, decreaseDebounce, baseThresholds,
+ increaseThresholds, decreaseThresholds);
+ }
+
+ private static float getFloat(Resources resources, int id) {
+ TypedValue value = new TypedValue();
+ resources.getValue(id, value, true /* resolveRefs */);
+ if (value.type != TypedValue.TYPE_FLOAT) {
+ return Float.NaN;
+ }
+ return value.getFloat();
+ }
+
+ private static float[] getFloatArray(Resources resources, int id) {
+ TypedArray array = resources.obtainTypedArray(id);
+ try {
+ if (array.length() == 0) {
+ return null;
+ }
+ float[] values = new float[array.length()];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = array.getFloat(i, Float.NaN);
+ if (Float.isNaN(values[i])) {
+ return null;
+ }
+ }
+ return values;
+ } finally {
+ array.recycle();
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
new file mode 100644
index 0000000..a53e91c
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2019 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.display.whitebalance;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings.Secure;
+import android.util.Slog;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.display.ColorDisplayService;
+import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController.Callbacks;
+
+import java.io.PrintWriter;
+
+/**
+ * The DisplayWhiteBalanceSettings holds the state of all the settings related to
+ * display white-balance, and can be used to decide whether to enable the
+ * DisplayWhiteBalanceController.
+ */
+public class DisplayWhiteBalanceSettings implements
+ ColorDisplayService.DisplayWhiteBalanceListener {
+
+ protected static final String TAG = "DisplayWhiteBalanceSettings";
+ protected boolean mLoggingEnabled;
+
+ private static final String SETTING_URI = Secure.DISPLAY_WHITE_BALANCE_ENABLED;
+ private static final int SETTING_DEFAULT = 0;
+ private static final int SETTING_ENABLED = 1;
+
+ private static final int MSG_SET_ACTIVE = 1;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final SettingsObserver mSettingsObserver;
+
+ // To decouple the DisplayPowerController from the DisplayWhiteBalanceSettings, the DPC
+ // implements Callbacks and passes itself to the DWBS so it can call back into it without
+ // knowing about it.
+ private Callbacks mCallbacks;
+
+ private int mSetting;
+ private boolean mActive;
+
+ /**
+ * @param context
+ * The context in which display white-balance is used.
+ * @param handler
+ * The handler used to determine which thread to run on.
+ *
+ * @throws NullPointerException
+ * - context is null;
+ * - handler is null.
+ */
+ public DisplayWhiteBalanceSettings(@NonNull Context context, @NonNull Handler handler) {
+ validateArguments(context, handler);
+ mLoggingEnabled = false;
+ mContext = context;
+ mHandler = new DisplayWhiteBalanceSettingsHandler(handler.getLooper());
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSetting = getSetting();
+ mActive = false;
+ mCallbacks = null;
+
+ mContext.getContentResolver().registerContentObserver(
+ Secure.getUriFor(SETTING_URI), false /* notifyForDescendants */, mSettingsObserver,
+ UserHandle.USER_ALL);
+
+ ColorDisplayServiceInternal cds =
+ LocalServices.getService(ColorDisplayServiceInternal.class);
+ cds.setDisplayWhiteBalanceListener(this);
+ }
+
+ /**
+ * Set an object to call back to when the display white balance state should be updated.
+ *
+ * @param callbacks
+ * The object to call back to.
+ *
+ * @return Whether the method suceeded or not.
+ */
+ public boolean setCallbacks(Callbacks callbacks) {
+ if (mCallbacks == callbacks) {
+ return false;
+ }
+ mCallbacks = callbacks;
+ return true;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Returns whether display white-balance is enabled.
+ *
+ * @return Whether display white-balance is enabled.
+ */
+ public boolean isEnabled() {
+ return (mSetting == SETTING_ENABLED) && mActive;
+ }
+
+ /**
+ * Re-evaluate state after switching to a new user.
+ */
+ public void onSwitchUser() {
+ handleSettingChange();
+ }
+
+ /**
+ * Dump the state.
+ *
+ * @param writer
+ * The writer used to dump the state.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println("DisplayWhiteBalanceSettings");
+ writer.println(" mLoggingEnabled=" + mLoggingEnabled);
+ writer.println(" mContext=" + mContext);
+ writer.println(" mHandler=" + mHandler);
+ writer.println(" mSettingsObserver=" + mSettingsObserver);
+ writer.println(" mSetting=" + mSetting);
+ writer.println(" mActive=" + mActive);
+ writer.println(" mCallbacks=" + mCallbacks);
+ }
+
+ @Override
+ public void onDisplayWhiteBalanceStatusChanged(boolean active) {
+ Message msg = mHandler.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0);
+ msg.sendToTarget();
+ }
+
+ private void validateArguments(Context context, Handler handler) {
+ Preconditions.checkNotNull(context, "context must not be null");
+ Preconditions.checkNotNull(handler, "handler must not be null");
+ }
+
+ private int getSetting() {
+ return Secure.getIntForUser(mContext.getContentResolver(), SETTING_URI, SETTING_DEFAULT,
+ UserHandle.USER_CURRENT);
+ }
+
+ private void handleSettingChange() {
+ final int setting = getSetting();
+ if (mSetting == setting) {
+ return;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "Setting: " + setting);
+ }
+ mSetting = setting;
+ if (mCallbacks != null) {
+ mCallbacks.updateWhiteBalance();
+ }
+ }
+
+ private void setActive(boolean active) {
+ if (mActive == active) {
+ return;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "Active: " + active);
+ }
+ mActive = active;
+ if (mCallbacks != null) {
+ mCallbacks.updateWhiteBalance();
+ }
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ handleSettingChange();
+ }
+ }
+
+ private final class DisplayWhiteBalanceSettingsHandler extends Handler {
+ DisplayWhiteBalanceSettingsHandler(Looper looper) {
+ super(looper, null, true /* async */);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_ACTIVE:
+ setActive(msg.arg1 != 0);
+ break;
+ }
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
new file mode 100644
index 0000000..c1f0e98
--- /dev/null
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2019 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.display.whitebalance;
+
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the
+ * screen color temperature should be updated, suppressing changes that are too frequent or too
+ * minor.
+ */
+class DisplayWhiteBalanceThrottler {
+
+ protected static final String TAG = "DisplayWhiteBalanceThrottler";
+ protected boolean mLoggingEnabled;
+
+ private int mIncreaseDebounce; // Milliseconds
+ private int mDecreaseDebounce; // Milliseconds
+ private long mLastTime; // Milliseconds
+
+ private float[] mBaseThresholds;
+ private float[] mIncreaseThresholds;
+ private float[] mDecreaseThresholds;
+ private float mIncreaseThreshold;
+ private float mDecreaseThreshold;
+ private float mLastValue;
+
+ /**
+ * @param increaseDebounce
+ * The debounce time for increasing (throttled if {@code time < lastTime + debounce}).
+ * @param decreaseDebounce
+ * The debounce time for decreasing (throttled if {@code time < lastTime + debounce}).
+ * @param baseThresholds
+ * The ambient color temperature values used to determine the threshold as the
+ * corresponding value in increaseThresholds/decreaseThresholds.
+ * @param increaseThresholds
+ * The increase threshold values (throttled if {@code value < value * (1 + threshold)}).
+ * @param decreaseThresholds
+ * The decrease threshold values (throttled if {@code value > value * (1 - threshold)}).
+ *
+ * @throws IllegalArgumentException
+ * - increaseDebounce is negative;
+ * - decreaseDebounce is negative;
+ * - baseThresholds to increaseThresholds is not a valid mapping*;
+ * - baseThresholds to decreaseThresholds is not a valid mapping*;
+ *
+ * (*) The x to y mapping is valid if:
+ * - x and y are not null;
+ * - x and y are not empty;
+ * - x and y contain only non-negative numbers;
+ * - x is strictly increasing.
+ */
+ DisplayWhiteBalanceThrottler(int increaseDebounce, int decreaseDebounce,
+ float[] baseThresholds, float[] increaseThresholds, float[] decreaseThresholds) {
+ validateArguments(increaseDebounce, decreaseDebounce, baseThresholds, increaseThresholds,
+ decreaseThresholds);
+ mLoggingEnabled = false;
+ mIncreaseDebounce = increaseDebounce;
+ mDecreaseDebounce = decreaseDebounce;
+ mBaseThresholds = baseThresholds;
+ mIncreaseThresholds = increaseThresholds;
+ mDecreaseThresholds = decreaseThresholds;
+ clear();
+ }
+
+ /**
+ * Check whether the ambient color temperature should be throttled.
+ *
+ * @param value
+ * The ambient color temperature value.
+ *
+ * @return Whether the ambient color temperature should be throttled.
+ */
+ public boolean throttle(float value) {
+ if (mLastTime != -1 && (tooSoon(value) || tooClose(value))) {
+ return true;
+ }
+ computeThresholds(value);
+ mLastTime = System.currentTimeMillis();
+ mLastValue = value;
+ return false;
+ }
+
+ /**
+ * Clears the throttler state.
+ */
+ public void clear() {
+ mLastTime = -1;
+ mIncreaseThreshold = -1.0f;
+ mDecreaseThreshold = -1.0f;
+ mLastValue = -1.0f;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging is on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
+ * Dump the state.
+ *
+ * @param writer
+ * The PrintWriter used to dump the state.
+ */
+ public void dump(PrintWriter writer) {
+ writer.println(" DisplayWhiteBalanceThrottler");
+ writer.println(" mLoggingEnabled=" + mLoggingEnabled);
+ writer.println(" mIncreaseDebounce=" + mIncreaseDebounce);
+ writer.println(" mDecreaseDebounce=" + mDecreaseDebounce);
+ writer.println(" mLastTime=" + mLastTime);
+ writer.println(" mBaseThresholds=" + Arrays.toString(mBaseThresholds));
+ writer.println(" mIncreaseThresholds=" + Arrays.toString(mIncreaseThresholds));
+ writer.println(" mDecreaseThresholds=" + Arrays.toString(mDecreaseThresholds));
+ writer.println(" mIncreaseThreshold=" + mIncreaseThreshold);
+ writer.println(" mDecreaseThreshold=" + mDecreaseThreshold);
+ writer.println(" mLastValue=" + mLastValue);
+ }
+
+ private void validateArguments(float increaseDebounce, float decreaseDebounce,
+ float[] baseThresholds, float[] increaseThresholds, float[] decreaseThresholds) {
+ if (Float.isNaN(increaseDebounce) || increaseDebounce < 0.0f) {
+ throw new IllegalArgumentException("increaseDebounce must be a non-negative number.");
+ }
+ if (Float.isNaN(decreaseDebounce) || decreaseDebounce < 0.0f) {
+ throw new IllegalArgumentException("decreaseDebounce must be a non-negative number.");
+ }
+ if (!isValidMapping(baseThresholds, increaseThresholds)) {
+ throw new IllegalArgumentException(
+ "baseThresholds to increaseThresholds is not a valid mapping.");
+ }
+ if (!isValidMapping(baseThresholds, decreaseThresholds)) {
+ throw new IllegalArgumentException(
+ "baseThresholds to decreaseThresholds is not a valid mapping.");
+ }
+ }
+
+ private static boolean isValidMapping(float[] x, float[] y) {
+ if (x == null || y == null || x.length == 0 || y.length == 0 || x.length != y.length) {
+ return false;
+ }
+ float prevX = -1.0f;
+ for (int i = 0; i < x.length; i++) {
+ if (Float.isNaN(x[i]) || Float.isNaN(y[i]) || x[i] < 0 || prevX >= x[i]) {
+ return false;
+ }
+ prevX = x[i];
+ }
+ return true;
+ }
+
+ private boolean tooSoon(float value) {
+ final long time = System.currentTimeMillis();
+ final long earliestTime;
+ if (value > mLastValue) {
+ earliestTime = mLastTime + mIncreaseDebounce;
+ } else { // value <= mLastValue
+ earliestTime = mLastTime + mDecreaseDebounce;
+ }
+ final boolean tooSoon = time < earliestTime;
+ if (mLoggingEnabled) {
+ Slog.d(TAG, (tooSoon ? "too soon: " : "late enough: ") + time
+ + (tooSoon ? " < " : " > ") + earliestTime);
+ }
+ return tooSoon;
+ }
+
+ private boolean tooClose(float value) {
+ final float threshold;
+ final boolean tooClose;
+ if (value > mLastValue) {
+ threshold = mIncreaseThreshold;
+ tooClose = value < threshold;
+ } else { // value <= mLastValue
+ threshold = mDecreaseThreshold;
+ tooClose = value > threshold;
+ }
+ if (mLoggingEnabled) {
+ Slog.d(TAG, (tooClose ? "too close: " : "far enough: ") + value
+ + (value > threshold ? " > " : " < ") + threshold);
+ }
+ return tooClose;
+ }
+
+ private void computeThresholds(float value) {
+ final int index = getHighestIndexBefore(value, mBaseThresholds);
+ mIncreaseThreshold = value * (1.0f + mIncreaseThresholds[index]);
+ mDecreaseThreshold = value * (1.0f - mDecreaseThresholds[index]);
+ }
+
+ private int getHighestIndexBefore(float value, float[] values) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] >= value) {
+ return i;
+ }
+ }
+ return values.length - 1;
+ }
+
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index d346ddc..ab9f711 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -332,9 +332,6 @@
// true if we started navigation
private boolean mStarted;
- // true if single shot request is in progress
- private boolean mSingleShot;
-
// capabilities of the GPS engine
private int mEngineCapabilities;
@@ -455,7 +452,7 @@
switch (action) {
case ALARM_WAKEUP:
- startNavigating(false);
+ startNavigating();
break;
case ALARM_TIMEOUT:
hibernate();
@@ -852,10 +849,9 @@
* allowed mode from properties.
*
* @param agpsEnabled whether AGPS is enabled by settings value
- * @param singleShot whether "singleshot" is needed
* @return SUPL mode (MSA vs MSB vs STANDALONE)
*/
- private int getSuplMode(boolean agpsEnabled, boolean singleShot) {
+ private int getSuplMode(boolean agpsEnabled) {
if (agpsEnabled) {
int suplMode = mGnssConfiguration.getSuplMode(0);
if (suplMode == 0) {
@@ -867,14 +863,6 @@
if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
return GPS_POSITION_MODE_MS_BASED;
}
- // for now, just as the legacy code did, we fallback to MS-Assisted if it is available,
- // do fallback only for single-shot requests, because it is too expensive to do for
- // periodic requests as well
- if (singleShot
- && hasCapability(GPS_CAPABILITY_MSA)
- && (suplMode & AGPS_SUPL_MODE_MSA) != 0) {
- return GPS_POSITION_MODE_MS_ASSISTED;
- }
}
return GPS_POSITION_MODE_STANDALONE;
}
@@ -965,22 +953,6 @@
return;
}
- boolean singleShot = false;
-
- // see if the request is for a single update
- if (mProviderRequest.locationRequests != null
- && mProviderRequest.locationRequests.size() > 0) {
- // if any request has zero or more than one updates
- // requested, then this is not single-shot mode
- singleShot = true;
-
- for (LocationRequest lr : mProviderRequest.locationRequests) {
- if (lr.getNumUpdates() != 1) {
- singleShot = false;
- }
- }
- }
-
if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) {
// update client uids
@@ -1003,7 +975,7 @@
}
} else if (!mStarted) {
// start GPS
- startNavigating(singleShot);
+ startNavigating();
} else {
// GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
mAlarmManager.cancel(mTimeoutIntent);
@@ -1148,13 +1120,12 @@
}
}
- private void startNavigating(boolean singleShot) {
+ private void startNavigating() {
if (!mStarted) {
- if (DEBUG) Log.d(TAG, "startNavigating, singleShot is " + singleShot);
+ if (DEBUG) Log.d(TAG, "startNavigating");
mTimeToFirstFix = 0;
mLastFixTime = 0;
mStarted = true;
- mSingleShot = singleShot;
mPositionMode = GPS_POSITION_MODE_STANDALONE;
// Notify about suppressed output, if speed limit was previously exceeded.
// Elsewhere, we check again with every speed output reported.
@@ -1166,7 +1137,7 @@
boolean agpsEnabled =
(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
- mPositionMode = getSuplMode(agpsEnabled, singleShot);
+ mPositionMode = getSuplMode(agpsEnabled);
if (DEBUG) {
String mode;
@@ -1221,7 +1192,6 @@
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
- mSingleShot = false;
native_stop();
mLastFixTime = 0;
// native_stop() may reset the position mode in hardware.
@@ -1300,10 +1270,6 @@
mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
}
- if (mSingleShot) {
- stopNavigating();
- }
-
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// For devices that use framework scheduling, a timer may be set to ensure we don't
// spend too much power searching for a location, when the requested update rate is
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 1cbf0bf..b0d2704 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -270,9 +270,7 @@
Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
}
- if (updateAllOverlaysForTarget(packageName, userId, 0)) {
- mListener.onOverlaysChanged(packageName, userId);
- }
+ updateAllOverlaysForTarget(packageName, userId, 0);
}
void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 94b1b36..5b38208 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -507,7 +507,7 @@
int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
// When an app or priv app is configured to run out of box, only verify it.
- if (info.isCodeIntegrityPreferred()
+ if (info.isEmbeddedDexUsed()
|| (info.isPrivilegedApp()
&& DexManager.isPackageSelectedToRunOob(info.packageName))) {
return "verify";
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a3e0d8d..af50c87 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -204,13 +204,17 @@
mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
mSessionsDir.mkdirs();
- mStagingManager = new StagingManager(pm);
+ mStagingManager = new StagingManager(pm, this);
}
private void setBootCompleted() {
mBootCompleted = true;
}
+ boolean isBootCompleted() {
+ return mBootCompleted;
+ }
+
public void systemReady() {
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -245,13 +249,24 @@
// the updated information.
writeSessionsLocked();
+ }
+ }
+
+ void restoreAndApplyStagedSessionIfNeeded() {
+ List<PackageInstallerSession> stagedSessionsToRestore = new ArrayList<>();
+ synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
if (session.isStaged()) {
- mStagingManager.restoreSession(session);
+ stagedSessionsToRestore.add(session);
}
}
}
+ // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
+ // atomic install which needs to query sessions, which requires lock on mSessions.
+ for (PackageInstallerSession session : stagedSessionsToRestore) {
+ mStagingManager.restoreSession(session);
+ }
}
@GuardedBy("mSessions")
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index de0849f..dc6751c4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1563,12 +1563,12 @@
}
}
}
- if (baseApk.preferCodeIntegrity) {
+ if (baseApk.useEmbeddedDex) {
for (File file : mResolvedStagedFiles) {
if (file.getName().endsWith(".apk")
- && !DexManager.auditUncompressedCodeInApk(file.getPath())) {
+ && !DexManager.auditUncompressedDexInApk(file.getPath())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Some code are not uncompressed and aligned correctly for "
+ "Some dex are not uncompressed and aligned correctly for "
+ mPackageName);
}
}
@@ -1664,6 +1664,12 @@
}
}
+ String getInstallerPackageName() {
+ synchronized (mLock) {
+ return mInstallerPackageName;
+ }
+ }
+
private static String getRelativePath(File file, File base) throws IOException {
final String pathStr = file.getAbsolutePath();
final String baseStr = base.getAbsolutePath();
@@ -1964,7 +1970,7 @@
// Send broadcast to default launcher only if it's a new install
final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
- if (success && isNewInstall) {
+ if (success && isNewInstall && mPm.mInstallerService.isBootCompleted()) {
mPm.sendSessionCommitBroadcast(generateInfo(), userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 949a111..e370e6b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20754,6 +20754,11 @@
}
mModuleInfoProvider.systemReady();
+
+ // Installer service might attempt to install some packages that have been staged for
+ // installation on reboot. Make sure this is the last component to be call since the
+ // installation might require other components to be ready.
+ mInstallerService.restoreAndApplyStagedSessionIfNeeded();
}
public void waitForAppDataPrepared() {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index c4d27e5..84fd01a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -21,6 +21,10 @@
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
import android.apex.IApexService;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
@@ -29,7 +33,10 @@
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
@@ -40,9 +47,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -53,14 +64,16 @@
private static final String TAG = "StagingManager";
+ private final PackageInstallerService mPi;
private final PackageManagerService mPm;
private final Handler mBgHandler;
@GuardedBy("mStagedSessions")
private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
- StagingManager(PackageManagerService pm) {
+ StagingManager(PackageManagerService pm, PackageInstallerService pi) {
mPm = pm;
+ mPi = pi;
mBgHandler = BackgroundThread.getHandler();
}
@@ -85,7 +98,7 @@
return new ParceledListSlice<>(result);
}
- private static boolean validateApexSignatureLocked(String apexPath, String packageName) {
+ private static boolean validateApexSignature(String apexPath, String packageName) {
final SigningDetails signingDetails;
try {
signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
@@ -173,6 +186,13 @@
private void preRebootVerification(@NonNull PackageInstallerSession session) {
boolean success = true;
+ if (!sessionContainsApex(session)) {
+ // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
+ // right away.
+ session.setStagedSessionReady();
+ return;
+ }
+
final ApexInfoList apexInfoList = new ApexInfoList();
// APEX checks. For single-package sessions, check if they contain an APEX. For
// multi-package sessions, find all the child sessions that contain an APEX.
@@ -199,16 +219,13 @@
"APEX staging failed, check logcat messages from apexd for more details.");
}
- if (apexInfoList.apexInfos.length > 0) {
+ if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) {
// For APEXes, we validate the signature here before we mark the session as ready,
// so we fail the session early if there is a signature mismatch. For APKs, the
// signature verification will be done by the package manager at the point at which
// it applies the staged install.
- //
- // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
- // right away.
for (ApexInfo apexPackage : apexInfoList.apexInfos) {
- if (!validateApexSignatureLocked(apexPackage.packagePath,
+ if (!validateApexSignature(apexPackage.packagePath,
apexPackage.packageName)) {
session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED,
"APK-container signature verification failed for package "
@@ -229,35 +246,182 @@
}
}
+ private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
+ if (!session.isMultiPackage()) {
+ return isApexSession(session);
+ }
+ synchronized (mStagedSessions) {
+ return !(Arrays.stream(session.getChildSessionIds())
+ // Retrieve cached sessions matching ids.
+ .mapToObj(i -> mStagedSessions.get(i))
+ // Filter only the ones containing APEX.
+ .filter(childSession -> isApexSession(childSession))
+ .collect(Collectors.toList())
+ .isEmpty());
+ }
+ }
+
private void resumeSession(@NonNull PackageInstallerSession session) {
- // Check with apexservice whether the apex
- // packages have been activated.
- final IApexService apex = IApexService.Stub.asInterface(
- ServiceManager.getService("apexservice"));
- ApexSessionInfo apexSessionInfo;
- try {
- apexSessionInfo = apex.getStagedSessionInfo(session.sessionId);
- } catch (RemoteException re) {
- Slog.e(TAG, "Unable to contact apexservice", re);
- // TODO should we retry here? Mark the session as failed?
+ if (sessionContainsApex(session)) {
+ // Check with apexservice whether the apex
+ // packages have been activated.
+ final IApexService apex = IApexService.Stub.asInterface(
+ ServiceManager.getService("apexservice"));
+ ApexSessionInfo apexSessionInfo;
+ try {
+ apexSessionInfo = apex.getStagedSessionInfo(session.sessionId);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to contact apexservice", re);
+ // TODO should we retry here? Mark the session as failed?
+ return;
+ }
+ if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
+ session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED,
+ "APEX activation failed. Check logcat messages from apexd for "
+ + "more information.");
+ return;
+ }
+ if (apexSessionInfo.isVerified) {
+ // Session has been previously submitted to apexd, but didn't complete all the
+ // pre-reboot verification, perhaps because the device rebooted in the meantime.
+ // Greedily re-trigger the pre-reboot verification.
+ Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
+ + "verified, resuming pre-reboot verification");
+ mBgHandler.post(() -> preRebootVerification(session));
+ return;
+ }
+ if (!apexSessionInfo.isActivated) {
+ // In all the remaining cases apexd will try to apply the session again at next
+ // boot. Nothing to do here for now.
+ Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied "
+ + "at boot didn't activate nor fail. This usually means that apexd will "
+ + "retry at next reboot.");
+ return;
+ }
+ }
+ // The APEX part of the session is activated, proceed with the installation of APKs.
+ if (!installApksInSession(session)) {
+ session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED,
+ "APK installation for staged session " + session.sessionId + " failed.");
return;
}
- if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) {
- session.setStagedSessionFailed(SessionInfo.ACTIVATION_FAILED,
- "APEX activation failed. Check logcat messages from apexd for "
- + "more information.");
+ session.setStagedSessionApplied();
+ }
+
+ private String findFirstAPKInDir(File stageDir) {
+ if (stageDir != null && stageDir.exists()) {
+ for (File file : stageDir.listFiles()) {
+ if (file.getAbsolutePath().toLowerCase().endsWith(".apk")) {
+ return file.getAbsolutePath();
+ }
+ }
}
- if (apexSessionInfo.isVerified) {
- // Session has been previously submitted to apexd, but didn't complete all the
- // pre-reboot verification, perhaps because the device rebooted in the meantime.
- // Greedily re-trigger the pre-reboot verification.
- mBgHandler.post(() -> preRebootVerification(session));
+ return null;
+ }
+
+ private PackageInstallerSession createAndWriteApkSession(
+ @NonNull PackageInstallerSession originalSession) {
+ // TODO(b/123629153): support split APKs.
+ if (originalSession.stageDir == null) {
+ Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
+ return null;
}
- if (apexSessionInfo.isActivated) {
- session.setStagedSessionApplied();
- // TODO(b/118865310) if multi-package proceed with the installation of APKs.
+ String apkFilePath = findFirstAPKInDir(originalSession.stageDir);
+ if (apkFilePath == null) {
+ Slog.w(TAG, "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath());
+ return null;
}
- // In every other case apexd will retry to apply the session at next boot.
+ File apkFile = new File(apkFilePath);
+
+ PackageInstaller.SessionParams params = originalSession.params.copy();
+ params.isStaged = false;
+ int apkSessionId = mPi.createSession(
+ params, originalSession.getInstallerPackageName(), originalSession.userId);
+ PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
+
+ try {
+ apkSession.open();
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ long sizeBytes = pfd.getStatSize();
+ if (sizeBytes < 0) {
+ Slog.e(TAG, "Unable to get size of: " + apkFilePath);
+ return null;
+ }
+ apkSession.write(apkFile.getName(), 0, sizeBytes, pfd);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e);
+ return null;
+ }
+ return apkSession;
+ }
+
+ private boolean commitApkSession(@NonNull PackageInstallerSession apkSession,
+ int originalSessionId) {
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ apkSession.commit(receiver.getIntentSender(), false);
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ return true;
+ }
+ Slog.e(TAG, "Failure to install APK staged session " + originalSessionId + " ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return false;
+ }
+
+ private boolean installApksInSession(@NonNull PackageInstallerSession session) {
+ if (!session.isMultiPackage() && !isApexSession(session)) {
+ // APK single-packaged staged session. Do a regular install.
+ PackageInstallerSession apkSession = createAndWriteApkSession(session);
+ if (apkSession == null) {
+ return false;
+ }
+ return commitApkSession(apkSession, session.sessionId);
+ } else if (session.isMultiPackage()) {
+ // For multi-package staged sessions containing APKs, we identify which child sessions
+ // contain an APK, and with those then create a new multi-package group of sessions,
+ // carrying over all the session parameters and unmarking them as staged. On commit the
+ // sessions will be installed atomically.
+ List<PackageInstallerSession> childSessions;
+ synchronized (mStagedSessions) {
+ childSessions =
+ Arrays.stream(session.getChildSessionIds())
+ // Retrieve cached sessions matching ids.
+ .mapToObj(i -> mStagedSessions.get(i))
+ // Filter only the ones containing APKs.s
+ .filter(childSession -> !isApexSession(childSession))
+ .collect(Collectors.toList());
+ }
+ if (childSessions.isEmpty()) {
+ // APEX-only multi-package staged session, nothing to do.
+ return true;
+ }
+ PackageInstaller.SessionParams params = session.params.copy();
+ params.isStaged = false;
+ int apkParentSessionId = mPi.createSession(
+ params, session.getInstallerPackageName(), session.userId);
+ PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
+ try {
+ apkParentSession.open();
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to prepare multi-package session for staged session "
+ + session.sessionId);
+ return false;
+ }
+
+ for (PackageInstallerSession sessionToClone : childSessions) {
+ PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone);
+ if (apkChildSession == null) {
+ return false;
+ }
+ apkParentSession.addChildSessionId(apkChildSession.sessionId);
+ }
+ return commitApkSession(apkParentSession, session.sessionId);
+ }
+ // APEX single-package staged session, nothing to do.
+ return true;
}
void commitSession(@NonNull PackageInstallerSession session) {
@@ -336,9 +500,36 @@
} else {
// Session had already being marked ready. Start the checks to verify if there is any
// follow-up work.
- // TODO(b/118865310): should this be synchronous to ensure it completes before
- // systemReady() finishes?
- mBgHandler.post(() -> resumeSession(session));
+ resumeSession(session);
+ }
+ }
+
+ private static class LocalIntentReceiver {
+ private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission,
+ Bundle options) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 7d2dd65..23705db 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -480,7 +480,7 @@
final String apkPath = pkg.baseCodePath;
final ApplicationInfo appInfo = pkg.applicationInfo;
final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
- if (appInfo.isPrivilegedApp() || appInfo.isCodeIntegrityPreferred()) {
+ if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed()) {
// Privileged apps prefer to load trusted code so they don't use compiled views.
// If the app is not privileged but prefers code integrity, also avoid compiling
// views.
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index abbddf3..2213901 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -740,10 +740,10 @@
}
/**
- * Generates log if the archive located at {@code fileName} has uncompressed dex file and so
- * files that can be direclty mapped.
+ * Generates log if the archive located at {@code fileName} has uncompressed dex file that can
+ * be direclty mapped.
*/
- public static boolean auditUncompressedCodeInApk(String fileName) {
+ public static boolean auditUncompressedDexInApk(String fileName) {
StrictJarFile jarFile = null;
try {
jarFile = new StrictJarFile(fileName,
@@ -762,16 +762,6 @@
Slog.w(TAG, "APK " + fileName + " has unaligned dex code " +
entry.getName());
}
- } else if (entry.getName().endsWith(".so")) {
- if (entry.getMethod() != ZipEntry.STORED) {
- allCorrect = false;
- Slog.w(TAG, "APK " + fileName + " has compressed native code " +
- entry.getName());
- } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
- allCorrect = false;
- Slog.w(TAG, "APK " + fileName + " has unaligned native code " +
- entry.getName());
- }
}
}
return allCorrect;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 8550bc3..1d74e1f 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -104,7 +104,6 @@
private static final String KEY_AOD_DISABLED = "aod_disabled";
// Go into deep Doze as soon as the screen turns off.
private static final String KEY_QUICK_DOZE_ENABLED = "quick_doze_enabled";
- private static final String KEY_SEND_TRON_LOG = "send_tron_log";
private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
@@ -129,8 +128,7 @@
new ArrayMap<>(), /* filesForNoninteractive */
false, /* forceAllAppsStandby */
false, /* forceBackgroundCheck */
- PowerManager.LOCATION_MODE_NO_CHANGE, /* gpsMode */
- false /* sendTronLog */
+ PowerManager.LOCATION_MODE_NO_CHANGE /* gpsMode */
);
private static final Policy DEFAULT_ADAPTIVE_POLICY = OFF_POLICY;
@@ -154,8 +152,7 @@
new ArrayMap<>(), /* filesForNoninteractive */
true, /* forceAllAppsStandby */
true, /* forceBackgroundCheck */
- PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, /* gpsMode */
- false /* sendTronLog */
+ PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF /* gpsMode */
);
private final Object mLock;
@@ -418,13 +415,10 @@
if (currPolicy.disableOptionalSensors) sb.append("S");
if (currPolicy.disableAod) sb.append("o");
if (currPolicy.enableQuickDoze) sb.append("q");
- if (currPolicy.sendTronLog) sb.append("t");
sb.append(currPolicy.gpsMode);
mEventLogKeys = sb.toString();
-
- mBatterySavingStats.setSendTronLog(currPolicy.sendTronLog);
}
static class Policy {
@@ -567,11 +561,6 @@
*/
public final int gpsMode;
- /**
- * Whether BatterySavingStats should send tron events.
- */
- public final boolean sendTronLog;
-
private final int mHashCode;
Policy(
@@ -593,8 +582,7 @@
ArrayMap<String, String> filesForNoninteractive,
boolean forceAllAppsStandby,
boolean forceBackgroundCheck,
- int gpsMode,
- boolean sendTronLog) {
+ int gpsMode) {
this.adjustBrightnessFactor = adjustBrightnessFactor;
this.advertiseIsEnabled = advertiseIsEnabled;
@@ -615,7 +603,6 @@
this.forceAllAppsStandby = forceAllAppsStandby;
this.forceBackgroundCheck = forceBackgroundCheck;
this.gpsMode = gpsMode;
- this.sendTronLog = sendTronLog;
mHashCode = Objects.hash(
adjustBrightnessFactor,
@@ -636,8 +623,7 @@
filesForNoninteractive,
forceAllAppsStandby,
forceBackgroundCheck,
- gpsMode,
- sendTronLog);
+ gpsMode);
}
static Policy fromConfig(BatterySaverPolicyConfig config) {
@@ -674,8 +660,7 @@
(new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
config.getForceAllAppsStandby(),
config.getForceBackgroundCheck(),
- config.getGpsMode(),
- OFF_POLICY.sendTronLog
+ config.getGpsMode()
);
}
@@ -737,7 +722,6 @@
boolean forceBackgroundCheck = parser.getBoolean(KEY_FORCE_BACKGROUND_CHECK,
defaultPolicy.forceBackgroundCheck);
int gpsMode = parser.getInt(KEY_GPS_MODE, defaultPolicy.gpsMode);
- boolean sendTronLog = parser.getBoolean(KEY_SEND_TRON_LOG, defaultPolicy.sendTronLog);
return new Policy(
adjustBrightnessFactor,
@@ -761,8 +745,7 @@
(new CpuFrequencies()).parseString(cpuFreqNoninteractive).toSysFileMap(),
forceAllAppsStandby,
forceBackgroundCheck,
- gpsMode,
- sendTronLog
+ gpsMode
);
}
@@ -788,7 +771,6 @@
&& forceAllAppsStandby == other.forceAllAppsStandby
&& forceBackgroundCheck == other.forceBackgroundCheck
&& gpsMode == other.gpsMode
- && sendTronLog == other.sendTronLog
&& filesForInteractive.equals(other.filesForInteractive)
&& filesForNoninteractive.equals(other.filesForNoninteractive);
}
@@ -1026,9 +1008,6 @@
pw.println(" " + KEY_SOUNDTRIGGER_DISABLED + "=" + p.disableSoundTrigger);
pw.print(indent);
pw.println(" " + KEY_QUICK_DOZE_ENABLED + "=" + p.enableQuickDoze);
- pw.print(indent);
- pw.println(" " + KEY_SEND_TRON_LOG + "=" + p.sendTronLog);
- pw.println();
pw.print(" Interactive File values:\n");
dumpMap(pw, " ", p.filesForInteractive);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 1d30a03..3dbc007 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -15,7 +15,6 @@
*/
package com.android.server.power.batterysaver;
-import android.metrics.LogMaker;
import android.os.BatteryManagerInternal;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -24,9 +23,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -142,7 +138,6 @@
}
private BatteryManagerInternal mBatteryManagerInternal;
- private final MetricsLogger mMetricsLogger;
private static final int STATE_NOT_INITIALIZED = -1;
private static final int STATE_CHARGING = -2;
@@ -172,28 +167,11 @@
@GuardedBy("mLock")
private long mLastBatterySaverDisabledTime = 0;
- private final MetricsLoggerHelper mMetricsLoggerHelper = new MetricsLoggerHelper();
-
- @VisibleForTesting
- @GuardedBy("mLock")
- private boolean mSendTronLog;
-
/** Visible for unit tests */
@VisibleForTesting
- public BatterySavingStats(Object lock, MetricsLogger metricsLogger) {
+ public BatterySavingStats(Object lock) {
mLock = lock;
mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
- mMetricsLogger = metricsLogger;
- }
-
- public BatterySavingStats(Object lock) {
- this(lock, new MetricsLogger());
- }
-
- public void setSendTronLog(boolean send) {
- synchronized (mLock) {
- mSendTronLog = send;
- }
}
private BatteryManagerInternal getBatteryManagerInternal() {
@@ -325,7 +303,6 @@
endLastStateLocked(now, batteryLevel, batteryPercent);
startNewStateLocked(newState, now, batteryLevel, batteryPercent);
- mMetricsLoggerHelper.transitionStateLocked(newState, now, batteryLevel, batteryPercent);
}
@GuardedBy("mLock")
@@ -472,61 +449,4 @@
onStat.totalBatteryDrainPercent,
onStat.drainPerHour() / 1000.0));
}
-
- @VisibleForTesting
- class MetricsLoggerHelper {
- private int mLastState = STATE_NOT_INITIALIZED;
- private long mStartTime;
- private int mStartBatteryLevel;
- private int mStartPercent;
-
- private static final int STATE_CHANGE_DETECT_MASK =
- (BatterySaverState.MASK << BatterySaverState.SHIFT) |
- (InteractiveState.MASK << InteractiveState.SHIFT);
-
- @GuardedBy("BatterySavingStats.this.mLock")
- public void transitionStateLocked(
- int newState, long now, int batteryLevel, int batteryPercent) {
- final boolean stateChanging =
- ((mLastState >= 0) ^ (newState >= 0)) ||
- (((mLastState ^ newState) & STATE_CHANGE_DETECT_MASK) != 0);
- if (stateChanging) {
- if (mLastState >= 0) {
- final long deltaTime = now - mStartTime;
-
- reportLocked(mLastState, deltaTime, mStartBatteryLevel, mStartPercent,
- batteryLevel, batteryPercent);
- }
- mStartTime = now;
- mStartBatteryLevel = batteryLevel;
- mStartPercent = batteryPercent;
- }
- mLastState = newState;
- }
-
- @GuardedBy("BatterySavingStats.this.mLock")
- void reportLocked(int state, long deltaTimeMs,
- int startBatteryLevelUa, int startBatteryLevelPercent,
- int endBatteryLevelUa, int endBatteryLevelPercent) {
- if (!mSendTronLog) {
- return;
- }
- final boolean batterySaverOn =
- BatterySaverState.fromIndex(state) != BatterySaverState.OFF;
- final boolean interactive =
- InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE;
-
- final LogMaker logMaker = new LogMaker(MetricsProto.MetricsEvent.BATTERY_SAVER)
- .setSubtype(batterySaverOn ? 1 : 0)
- .addTaggedData(MetricsEvent.FIELD_INTERACTIVE, interactive ? 1 : 0)
- .addTaggedData(MetricsEvent.FIELD_DURATION_MILLIS, deltaTimeMs)
- .addTaggedData(MetricsEvent.FIELD_START_BATTERY_UA, startBatteryLevelUa)
- .addTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT,
- startBatteryLevelPercent)
- .addTaggedData(MetricsEvent.FIELD_END_BATTERY_UA, endBatteryLevelUa)
- .addTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT, endBatteryLevelPercent);
-
- mMetricsLogger.write(logMaker);
- }
- }
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index b3cc6de..d00817f 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -18,14 +18,18 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Handler;
import android.os.HandlerThread;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.StatsLog;
+import com.android.internal.R;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
@@ -54,9 +58,15 @@
@Override
public int onHealthCheckFailed(String packageName, long versionCode) {
+ VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
+ if (moduleMetadataPackage == null) {
+ // Ignore failure, no mainline update available
+ return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ }
+
RollbackInfo rollback =
- getAvailableRollback(mContext.getSystemService(RollbackManager.class),
- packageName, versionCode);
+ getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class),
+ packageName, versionCode, moduleMetadataPackage);
if (rollback == null) {
// Don't handle the notification, no rollbacks available for the package
return PackageHealthObserverImpact.USER_IMPACT_NONE;
@@ -67,27 +77,41 @@
@Override
public boolean execute(String packageName, long versionCode) {
- RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
- RollbackInfo rollback = getAvailableRollback(rollbackManager, packageName, versionCode);
- if (rollback == null) {
- // Expected a rollback to be available, what happened?
+ VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
+ if (moduleMetadataPackage == null) {
+ // Ignore failure, no mainline update available
return false;
}
- // TODO(zezeozue): Only rollback if rollback version == failed package version
+ RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+ RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager,
+ packageName, versionCode, moduleMetadataPackage);
+ if (rollback == null) {
+ Slog.w(TAG, "Expected rollback but no rollback found for package: [ "
+ + packageName + "] with versionCode: [" + versionCode + "]");
+ return false;
+ }
+
+ StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+ moduleMetadataPackage.getPackageName(),
+ moduleMetadataPackage.getVersionCode());
LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == PackageInstaller.STATUS_SUCCESS) {
- // TODO(zezeozue); Log success metrics
- // Rolledback successfully, no action required by other observers
+ int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_FAILURE);
+ if (status == RollbackManager.STATUS_SUCCESS) {
+ StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ moduleMetadataPackage.getPackageName(),
+ moduleMetadataPackage.getVersionCode());
} else {
- // TODO(zezeozue); Log failure metrics
- // Rollback failed other observers should have a shot
+ StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ moduleMetadataPackage.getPackageName(),
+ moduleMetadataPackage.getVersionCode());
}
});
- // TODO(zezeozue): Log initiated metrics
mHandler.post(() ->
rollbackManager.commitRollback(rollback.getRollbackId(),
Collections.singletonList(new VersionedPackage(packageName, versionCode)),
@@ -109,17 +133,40 @@
PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
}
- private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
- String packageName, long versionCode) {
+ private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager,
+ String packageName, long versionCode, VersionedPackage moduleMetadataPackage) {
for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
+ // We only rollback mainline packages, so check if rollback contains the
+ // module metadata provider, if it does, the rollback is a mainline rollback
+ boolean hasModuleMetadataPackage = false;
+ boolean hasFailedPackage = false;
for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
- if (packageName.equals(packageRollback.getPackageName())
+ hasModuleMetadataPackage |= packageRollback.getPackageName().equals(
+ moduleMetadataPackage.getPackageName());
+ hasFailedPackage |= packageRollback.getPackageName().equals(packageName)
&& packageRollback.getVersionRolledBackFrom().getVersionCode()
- == versionCode) {
- return rollback;
- }
+ == versionCode;
+ }
+ if (hasModuleMetadataPackage && hasFailedPackage) {
+ return rollback;
}
}
return null;
}
+
+ private VersionedPackage getModuleMetadataPackage() {
+ String packageName = mContext.getResources().getString(
+ R.string.config_defaultModuleMetadataProvider);
+ if (!TextUtils.isEmpty(packageName)) {
+ return null;
+ }
+
+ try {
+ return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
+ packageName, 0 /* flags */).getLongVersionCode());
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Module metadata provider not found");
+ return null;
+ }
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f176bc4..8f5ce74 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -14078,6 +14078,10 @@
enforceCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
+ if (mInjector.settingsSecureGetIntForUser(
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, userHandle) == 0) {
+ return false;
+ }
final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
if (admin != null) {
if (admin.mCrossProfileCalendarPackages == null) {
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index d4bb636..089a79b 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -194,6 +194,7 @@
false /* sticky */,
false /* initialSticky */,
userId,
- false /* allowBackgroundActivityStarts */);
+ false, /* allowBackgroundActivityStarts */
+ false /* timeoutExempt */ );
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 535198b..9ac91dd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5278,6 +5278,35 @@
assertTrue(actual.containsAll(expected));
}
+ public void testIsPackageAllowedToAccessCalendar_adminNotAllowed() {
+ setAsProfileOwner(admin1);
+ dpm.setCrossProfileCalendarPackages(admin1, Collections.emptySet());
+ when(getServices().settings.settingsSecureGetIntForUser(
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+ 0, DpmMockContext.CALLER_USER_HANDLE)).thenReturn(1);
+ assertFalse(dpm.isPackageAllowedToAccessCalendar("TEST_PACKAGE"));
+ }
+
+ public void testIsPackageAllowedToAccessCalendar_settingOff() {
+ final String testPackage = "TEST_PACKAGE";
+ setAsProfileOwner(admin1);
+ dpm.setCrossProfileCalendarPackages(admin1, Collections.singleton(testPackage));
+ when(getServices().settings.settingsSecureGetIntForUser(
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+ 0, DpmMockContext.CALLER_USER_HANDLE)).thenReturn(0);
+ assertFalse(dpm.isPackageAllowedToAccessCalendar(testPackage));
+ }
+
+ public void testIsPackageAllowedToAccessCalendar_bothAllowed() {
+ final String testPackage = "TEST_PACKAGE";
+ setAsProfileOwner(admin1);
+ dpm.setCrossProfileCalendarPackages(admin1, null);
+ when(getServices().settings.settingsSecureGetIntForUser(
+ Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+ 0, DpmMockContext.CALLER_USER_HANDLE)).thenReturn(1);
+ assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage));
+ }
+
private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
final long ident = mServiceContext.binder.clearCallingIdentity();
mServiceContext.binder.callingUid =
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 9cc2b10..e32a789 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -21,8 +21,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
-
import android.content.Context;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
@@ -34,10 +32,8 @@
import com.android.frameworks.servicestests.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
import com.android.server.power.batterysaver.BatterySaverPolicy.Policy;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
@@ -87,9 +83,6 @@
}
}
- @Mock
- MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
-
private BatterySaverPolicyForTest mBatterySaverPolicy;
private final ArrayMap<String, String> mMockGlobalSettings = new ArrayMap<>();
@@ -100,7 +93,7 @@
MockitoAnnotations.initMocks(this);
final Object lock = new Object();
mBatterySaverPolicy = new BatterySaverPolicyForTest(lock, getContext(),
- new BatterySavingStats(lock, mMetricsLogger));
+ new BatterySavingStats(lock));
mBatterySaverPolicy.systemReady();
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index ba61fd2..76239fc 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -30,14 +30,12 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
@@ -54,7 +52,7 @@
private int mBatteryLevel = 1_000_000_000;
private BatterySavingStatsTestable() {
- super(new Object(), mMetricsLogger);
+ super(new Object());
}
@Override
@@ -104,23 +102,13 @@
public MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
- private boolean sendTronEvents;
-
@Test
- public void testAll_withTron() {
- sendTronEvents = true;
- checkAll();
- }
-
- @Test
- public void testAll_noTron() {
- sendTronEvents = false;
+ public void testAll() {
checkAll();
}
private void checkAll() {
final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
- target.setSendTronLog(sendTronEvents);
target.assertDumpable();
@@ -240,46 +228,13 @@
target.toDebugString());
}
- private void assertLog(boolean batterySaver, boolean interactive, long deltaTimeMs,
- int deltaBatteryLevelUa, int deltaBatteryLevelPercent) {
- if (sendTronEvents) {
- ArgumentCaptor<LogMaker> ac = ArgumentCaptor.forClass(LogMaker.class);
- verify(mMetricsLogger, times(1)).write(ac.capture());
-
- LogMaker lm = ac.getValue();
- assertEquals(MetricsEvent.BATTERY_SAVER, lm.getCategory());
- assertEquals(batterySaver ? 1 : 0,
- lm.getTaggedData(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE));
- assertEquals(interactive ? 1 : 0, lm.getTaggedData(MetricsEvent.FIELD_INTERACTIVE));
- assertEquals(deltaTimeMs, lm.getTaggedData(MetricsEvent.FIELD_DURATION_MILLIS));
-
- assertEquals(deltaBatteryLevelUa,
- (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_UA)
- - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_UA));
- assertEquals(deltaBatteryLevelPercent,
- (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT)
- - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT));
- } else {
- verify(mMetricsLogger, times(0)).write(any(LogMaker.class));
- }
- }
-
-
- @Test
- public void testMetricsLogger_withTron() {
- sendTronEvents = true;
- checkMetricsLogger();
+ private void assertLog() {
+ verify(mMetricsLogger, times(0)).write(any(LogMaker.class));
}
@Test
- public void testMetricsLogger_noTron() {
- sendTronEvents = false;
- checkMetricsLogger();
- }
-
- private void checkMetricsLogger() {
+ public void testMetricsLogger() {
final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
- target.setSendTronLog(sendTronEvents);
target.advanceClock(1);
target.drainBattery(1000);
@@ -300,7 +255,7 @@
InteractiveState.NON_INTERACTIVE,
DozeState.NOT_DOZING);
- assertLog(false, true, 60_000, 2000, 200);
+ assertLog();
target.advanceClock(1);
target.drainBattery(2000);
@@ -331,7 +286,7 @@
InteractiveState.INTERACTIVE,
DozeState.NOT_DOZING);
- assertLog(false, false, 60_000 * 3, 2000 * 3, 200 * 3);
+ assertLog();
target.advanceClock(10);
target.drainBattery(10000);
@@ -339,7 +294,7 @@
reset(mMetricsLogger);
target.startCharging();
- assertLog(true, true, 60_000 * 10, 10000, 1000);
+ assertLog();
target.advanceClock(1);
target.drainBattery(2000);
@@ -357,6 +312,6 @@
target.startCharging();
- assertLog(true, false, 60_000, 2000, 200);
+ assertLog();
}
}
diff --git a/startop/iorap/TEST_MAPPING b/startop/iorap/DISABLED_TEST_MAPPING
similarity index 100%
rename from startop/iorap/TEST_MAPPING
rename to startop/iorap/DISABLED_TEST_MAPPING
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index b2fa5d4..ef78ea5 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -22,6 +22,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -162,7 +163,9 @@
final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
- return filterByIntent(context, packageNames, dialIntentWithTelScheme, userId);
+ packageNames = filterByIntent(context, packageNames, dialIntentWithTelScheme, userId);
+ packageNames = requireInCallService(packageNames, userId, context);
+ return packageNames;
}
public static List<String> getInstalledDialerApplications(Context context) {
@@ -220,6 +223,35 @@
return result;
}
+ private static List<String> requireInCallService(List<String> packageNames, int userId,
+ Context context) {
+ if (packageNames == null || packageNames.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ final Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
+ final List<ResolveInfo> resolveInfoList = context.getPackageManager()
+ .queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, userId);
+ final List<String> result = new ArrayList<>();
+ final int length = resolveInfoList.size();
+ for (int i = 0; i < length; i++) {
+ final ServiceInfo info = resolveInfoList.get(i).serviceInfo;
+ if (info == null || info.metaData == null) {
+ continue;
+ }
+ if (!info.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_UI)) {
+ continue;
+ }
+ if (info.metaData.getBoolean(TelecomManager.METADATA_IN_CALL_SERVICE_CAR_MODE_UI)) {
+ continue;
+ }
+ if (packageNames.contains(info.packageName) && !result.contains(info.packageName)) {
+ result.add(info.packageName);
+ }
+ }
+
+ return result;
+ }
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index b2f5802..1bbf9f4 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -408,7 +408,7 @@
public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
private final Context mContext;
- private final int mCardId;
+ private int mCardId;
/** @hide */
public EuiccManager(Context context) {
@@ -446,7 +446,7 @@
public boolean isEnabled() {
// In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
// restrictions.
- return getIEuiccController() != null && mCardId != TelephonyManager.INVALID_CARD_ID;
+ return getIEuiccController() != null;
}
/**
@@ -456,11 +456,11 @@
* current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have
* access to the EID of another eUICC.
*
- * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready.
+ * @return the EID. May be null if the eUICC is not ready.
*/
@Nullable
public String getEid() {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
return null;
}
try {
@@ -475,15 +475,15 @@
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
*
- * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
- * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+ * @return the status of eUICC OTA. If the eUICC is not ready,
+ * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public int getOtaStatus() {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
return EUICC_OTA_STATUS_UNAVAILABLE;
}
try {
@@ -518,7 +518,7 @@
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void downloadSubscription(DownloadableSubscription subscription,
boolean switchAfterDownload, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -580,7 +580,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
PendingIntent callbackIntent =
resolutionIntent.getParcelableExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
@@ -617,7 +617,7 @@
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDownloadableSubscriptionMetadata(
DownloadableSubscription subscription, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -647,7 +647,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -662,12 +662,11 @@
/**
* Returns information about the eUICC chip/device.
*
- * @return the {@link EuiccInfo}. May be null if {@link #isEnabled()} is false or the eUICC is
- * not ready.
+ * @return the {@link EuiccInfo}. May be null if the eUICC is not ready.
*/
@Nullable
public EuiccInfo getEuiccInfo() {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
return null;
}
try {
@@ -692,7 +691,7 @@
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -732,7 +731,7 @@
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -758,7 +757,7 @@
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void updateSubscriptionNickname(
int subscriptionId, String nickname, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -782,7 +781,7 @@
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void eraseSubscriptions(PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -812,7 +811,7 @@
* @hide
*/
public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfInvalid()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -823,6 +822,19 @@
}
}
+ private boolean refreshCardIdIfInvalid() {
+ if (!isEnabled()) {
+ return false;
+ }
+ // Refresh mCardId if it's invalid.
+ if (mCardId == TelephonyManager.INVALID_CARD_ID) {
+ TelephonyManager tm = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mCardId = tm.getCardIdForDefaultEuicc();
+ }
+ return true;
+ }
+
private static void sendUnavailableError(PendingIntent callbackIntent) {
try {
callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index ca25386..4fd2043 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -14,7 +14,7 @@
</intent-filter>
</activity>
<service android:name="SchedulerService"
- android:foregroundServiceType="sync">
+ android:foregroundServiceType="dataSync|mediaPlayback|phoneCall|location|connectedDevice">
</service>
<service android:name="TestService" android:process=":test">
</service>
@@ -23,7 +23,6 @@
<receiver android:name="Receiver" android:exported="true">
</receiver>
</application>
-
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.frameworkperf"
android:label="Framework Perf Runner"
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index f330b83..1a4ec94 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -1027,5 +1027,15 @@
</intent-filter>
</activity>
+ <activity
+ android:name="PositionListenerActivity"
+ android:label="RenderNode/PositionListener"
+ android:screenOrientation="fullSensor">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.hwui.TEST" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
new file mode 100644
index 0000000..316aad3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.RenderNode;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PositionListenerActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
+ layout.addView(spinner);
+
+ ScrollView scrollingThing = new ScrollView(this);
+ scrollingThing.addView(new MyPositionReporter(this));
+ layout.addView(scrollingThing);
+
+ setContentView(layout);
+ }
+
+ static class MyPositionReporter extends TextView implements RenderNode.PositionUpdateListener {
+ RenderNode mNode;
+ int mCurrentCount = 0;
+ int mTranslateY = 0;
+
+ MyPositionReporter(Context c) {
+ super(c);
+ mNode = new RenderNode("positionListener");
+ mNode.requestPositionUpdates(this);
+ setTextAlignment(TEXT_ALIGNMENT_VIEW_START);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(getMeasuredWidth(), 10000);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mNode.setLeftTopRightBottom(left, top, right, bottom);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ ScrollView parent = (ScrollView) getParent();
+ canvas.translate(0, parent.getScrollY());
+ super.onDraw(canvas);
+ canvas.translate(0, -parent.getScrollY());
+ // Inject our listener proxy
+ canvas.drawRenderNode(mNode);
+ }
+
+ @Override
+ public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
+ post(() -> {
+ mCurrentCount++;
+ setText(String.format("%d: Position [%d, %d, %d, %d]", mCurrentCount,
+ left, top, right, bottom));
+ });
+ }
+
+ @Override
+ public void positionLost(long frameNumber) {
+ post(() -> {
+ mCurrentCount++;
+ setText(mCurrentCount + " No position");
+ });
+ }
+ }
+}
diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk
index 75282ba..89c21b7 100644
--- a/tests/JankBench/Android.mk
+++ b/tests/JankBench/Android.mk
@@ -19,7 +19,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.design_design \
+ com.google.android.material_material \
androidx.legacy_legacy-support-v4 \
androidx.appcompat_appcompat \
androidx.cardview_cardview \
diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
index 47db602..4de51fb 100644
--- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
+++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
@@ -19,22 +19,24 @@
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.design.widget.FloatingActionButton;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.Toast;
-import com.android.benchmark.registry.BenchmarkRegistry;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
import com.android.benchmark.R;
+import com.android.benchmark.registry.BenchmarkRegistry;
import com.android.benchmark.results.GlobalResultsStore;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
diff --git a/tests/JankBench/app/src/main/res/layout/activity_home.xml b/tests/JankBench/app/src/main/res/layout/activity_home.xml
index fb30747..160d9d7 100644
--- a/tests/JankBench/app/src/main/res/layout/activity_home.xml
+++ b/tests/JankBench/app/src/main/res/layout/activity_home.xml
@@ -23,7 +23,7 @@
android:fitsSystemWindows="true"
tools:context=".app.HomeActivity">
- <android.support.design.widget.AppBarLayout
+ <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
@@ -35,7 +35,7 @@
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
- </android.support.design.widget.AppBarLayout>
+ </com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
diff --git a/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml b/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml
index 3c72dc9..2f4813f 100644
--- a/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml
+++ b/tests/JankBench/app/src/main/res/layout/fragment_dashboard.xml
@@ -22,14 +22,14 @@
android:layout_height="fill_parent"
android:fitsSystemWindows="true">
- <android.support.design.widget.AppBarLayout
+ <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/detail_backdrop_height"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
- <android.support.design.widget.CollapsingToolbarLayout
+ <com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -55,9 +55,9 @@
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
- </android.support.design.widget.CollapsingToolbarLayout>
+ </com.google.android.material.appbar.CollapsingToolbarLayout>
- </android.support.design.widget.AppBarLayout>
+ </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
@@ -78,7 +78,7 @@
</androidx.core.widget.NestedScrollView>
- <android.support.design.widget.FloatingActionButton
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/start_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 4b277ae..ace0e6d 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -37,6 +37,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -628,9 +629,12 @@
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
+ // TODO: Allow installing test app along atomically with module metadata package so that
+ // a failed test app will be flagged as a failed mainline app
/**
* Test bad update automatic rollback.
*/
+ @Ignore
@Test
public void testBadUpdateRollback() throws Exception {
BroadcastReceiver crashCountReceiver = null;
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 6ec0fbb..608bf2f 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -15,7 +15,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.design_design \
+ com.google.android.material_material \
androidx.legacy_legacy-support-v4 \
androidx.appcompat_appcompat \
androidx.cardview_cardview \
diff --git a/tests/UiBench/res/layout/activity_navigation_drawer.xml b/tests/UiBench/res/layout/activity_navigation_drawer.xml
index 282fb78..383432a 100644
--- a/tests/UiBench/res/layout/activity_navigation_drawer.xml
+++ b/tests/UiBench/res/layout/activity_navigation_drawer.xml
@@ -27,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- <android.support.design.widget.NavigationView
+ <com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
diff --git a/tests/UiBench/res/layout/app_bar_navigation_drawer.xml b/tests/UiBench/res/layout/app_bar_navigation_drawer.xml
index b979134..cf209ac 100644
--- a/tests/UiBench/res/layout/app_bar_navigation_drawer.xml
+++ b/tests/UiBench/res/layout/app_bar_navigation_drawer.xml
@@ -21,7 +21,7 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
- <android.support.design.widget.AppBarLayout
+ <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
@@ -31,6 +31,6 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"/>
- </android.support.design.widget.AppBarLayout>
+ </com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java b/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
index 6e24ce1..2bf6040 100644
--- a/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/ClippedListActivity.java
@@ -16,18 +16,20 @@
package com.android.test.uibench;
import android.os.Bundle;
-import android.support.design.widget.NavigationView;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.ListFragment;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.ListFragment;
+
+import com.google.android.material.navigation.NavigationView;
+
public class ClippedListActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
diff --git a/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java b/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java
index 6a7761ce..7784539 100644
--- a/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/NavigationDrawerActivity.java
@@ -16,13 +16,15 @@
package com.android.test.uibench;
import android.os.Bundle;
-import android.support.design.widget.NavigationView;
-import androidx.core.view.GravityCompat;
-import androidx.drawerlayout.widget.DrawerLayout;
+import android.view.MenuItem;
+
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
-import android.view.MenuItem;
+import androidx.core.view.GravityCompat;
+import androidx.drawerlayout.widget.DrawerLayout;
+
+import com.google.android.material.navigation.NavigationView;
public class NavigationDrawerActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {