Merge "Add key layouts for Xbox clones" into rvc-dev
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 11d3a68..566f4cd 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -1114,20 +1114,22 @@
static {
// Using a LinkedHashMap to keep the insertion order when iterating over the keys.
LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
- extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
- extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
- extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
- extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
- extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
- extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
- extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
+ // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering,
+ // which in turn aims to minimize the chances of incorrect extractor selections.
extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new);
- extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+ extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new);
+ extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+ extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
+ extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
+ extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
+ extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
extractorFactoriesByName.put("exo.OggParser", OggExtractor::new);
extractorFactoriesByName.put("exo.PsParser", PsExtractor::new);
- extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
extractorFactoriesByName.put("exo.WavParser", WavExtractor::new);
+ extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
+ extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
+ extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
HashMap<String, Class> expectedTypeByParameterName = new HashMap<>();
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 205ffc2..30a8b45 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -246,7 +246,7 @@
permissionState.isGranted() && (permissionState.getFlags()
& PackageManager.FLAG_PERMISSION_ONE_TIME) == 0));
serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString(
- permissionState.getFlags() & ~PackageManager.FLAG_PERMISSION_ONE_TIME));
+ permissionState.getFlags()));
serializer.endTag(null, TAG_PERMISSION);
}
}
diff --git a/api/current.txt b/api/current.txt
index 1227006..a2f2c06 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8189,6 +8189,7 @@
field public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
field public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
field public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
+ field public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
}
public class AppWidgetProvider extends android.content.BroadcastReceiver {
@@ -29356,9 +29357,9 @@
public abstract class TvInputService extends android.app.Service {
ctor public TvInputService();
method public final android.os.IBinder onBind(android.content.Intent);
- method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String);
+ method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String);
method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
- method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String);
+ method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String);
method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64
field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190
@@ -48146,6 +48147,7 @@
method public String getMmsUserAgent();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
method public String getNetworkCountryIso();
+ method @NonNull public String getNetworkCountryIso(int);
method public String getNetworkOperator();
method public String getNetworkOperatorName();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
diff --git a/api/system-current.txt b/api/system-current.txt
index f84b415..3b95f32 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11738,7 +11738,6 @@
method public int getMaxNumberOfSimultaneouslyActiveSims();
method public static long getMaxNumberVerificationTimeoutMillis();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
- method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
diff --git a/api/test-current.txt b/api/test-current.txt
index 4a9c4d6..0f8694f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3769,7 +3769,6 @@
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method public int getEmergencyNumberDbVersion();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
- method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 95de6c5..1e200c5 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -146,6 +146,8 @@
struct uhid_event ev = {};
ev.type = UHID_CREATE2;
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
+ std::string uniq = android::base::StringPrintf("Id: %d", id);
+ strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
ev.u.create2.rd_size = size;
ev.u.create2.bus = bus;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2575542..f36b855 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -393,6 +393,8 @@
WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
SnapshotMergeReported snapshot_merge_reported = 255;
+ ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
+ 256 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -3285,12 +3287,12 @@
];
}
-/*
+/**
* Logs foreground service starts and stops.
* Note that this is not when a service starts or stops, but when it is
* considered foreground.
* Logged from
- * //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
*/
message ForegroundServiceStateChanged {
optional int32 uid = 1 [(is_uid) = true];
@@ -3302,6 +3304,49 @@
EXIT = 2;
}
optional State state = 3;
+
+ // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
+ // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
+ optional bool allow_while_in_use_permission = 4;
+}
+
+/**
+ * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
+ * A foreground service session is any continuous period during which the uid holds at least one
+ * foreground service; the atom will be pushed when the uid no longer holds any foreground services.
+ * Accesses initiated while the uid is in the TOP state are ignored.
+ * Sessions with no attempted accesses are not logged.
+ * Logged from
+ * frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ForegroundServiceAppOpSessionEnded {
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The operation's name.
+ // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
+ // Only these named ops are actually logged.
+ enum AppOpName {
+ OP_NONE = -1; // Also represents UNKNOWN.
+ OP_COARSE_LOCATION = 0;
+ OP_FINE_LOCATION = 1;
+ OP_CAMERA = 26;
+ OP_RECORD_AUDIO = 27;
+ }
+ optional AppOpName app_op_name = 2 [default = OP_NONE];
+
+ // The uid's permission mode for accessing the AppOp during this fgs session.
+ enum Mode {
+ MODE_UNKNOWN = 0;
+ MODE_ALLOWED = 1; // Always allowed
+ MODE_IGNORED = 2; // Denied
+ MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
+ }
+ optional Mode app_op_mode = 3;
+
+ // Number of times this AppOp was requested and allowed.
+ optional int32 count_ops_accepted = 4;
+ // Number of times this AppOp was requested but denied.
+ optional int32 count_ops_rejected = 5;
}
/**
@@ -8189,7 +8234,7 @@
*/
message CameraActionEvent {
// Camera session duration
- optional int64 duration = 1;
+ optional int64 duration_millis = 1;
// Camera API level used
optional int32 api_level = 2;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3b0667d..316a018 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -22,6 +22,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
@@ -61,6 +62,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Pattern;
/**
* This class describes an {@link AccessibilityService}. The system notifies an
@@ -552,6 +554,13 @@
*/
private int mHtmlDescriptionRes;
+ // Used for html description of accessibility service. The <img> src tag must follow the
+ // prefix rule. e.g. <img src="R.drawable.fileName"/>
+ private static final String IMG_PREFIX = "R.drawable.";
+ private static final String ANCHOR_TAG = "a";
+ private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>(
+ Collections.singletonList(ANCHOR_TAG));
+
/**
* Creates a new instance.
*/
@@ -782,12 +791,10 @@
}
/**
- * The animated image resource id.
- * <p>
- * <strong>Statically set from
- * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
- * </p>
+ * Gets the animated image resource id.
+ *
* @return The animated image resource id.
+ *
* @hide
*/
public int getAnimatedImageRes() {
@@ -797,10 +804,12 @@
/**
* The animated image drawable.
* <p>
+ * Image can not exceed the screen size.
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return The animated image drawable.
+ * @return The animated image drawable, or null if the resource is invalid or the image
+ * exceed the screen size.
*/
@Nullable
public Drawable loadAnimatedImage(@NonNull Context context) {
@@ -808,11 +817,8 @@
return null;
}
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = mComponentName.getPackageName();
- final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo;
-
- return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ return loadSafeAnimatedImage(context, mResolveInfo.serviceInfo.applicationInfo,
+ mAnimatedImageRes);
}
/**
@@ -924,16 +930,17 @@
}
/**
- * The localized html description of the accessibility service.
+ * The localized and restricted html description of the accessibility service.
* <p>
+ * Filters the <img> tag which do not meet the custom specification and the <a> tag.
* <strong>Statically set from
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
- * @return The localized html description.
+ * @return The localized and restricted html description.
*/
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
- if (mHtmlDescriptionRes == 0) {
+ if (mHtmlDescriptionRes == /* invalid */ 0) {
return null;
}
@@ -941,7 +948,7 @@
final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
mHtmlDescriptionRes, serviceInfo.applicationInfo);
if (htmlDescription != null) {
- return htmlDescription.toString().trim();
+ return getFilteredHtmlText(htmlDescription.toString().trim());
}
return null;
}
@@ -1414,4 +1421,103 @@
return new AccessibilityServiceInfo[size];
}
};
+
+ /**
+ * Gets the filtered html string for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters
+ * the <img> tag which do not meet the custom specification and the <a> tag.
+ *
+ * @param text the target text is html format.
+ * @return the filtered html string.
+ *
+ * @hide
+ */
+ public static @NonNull String getFilteredHtmlText(@NonNull String text) {
+ final String replacementStart = "<invalidtag ";
+ final String replacementEnd = "</invalidtag>";
+
+ for (String tag : UNSUPPORTED_TAG_LIST) {
+ final String regexStart = "(?i)<" + tag + "(\\s+|>)";
+ final String regexEnd = "(?i)</" + tag + "\\s*>";
+ text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart);
+ text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd);
+ }
+
+ final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")";
+ text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll(
+ replacementStart);
+
+ return text;
+ }
+
+ /**
+ * Loads the animated image for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource
+ * whether to exceed the screen size.
+ *
+ * @param context the current context.
+ * @param applicationInfo the current application.
+ * @param resId the animated image resource id.
+ * @return the animated image which is safe.
+ *
+ * @hide
+ */
+ @Nullable
+ public static Drawable loadSafeAnimatedImage(@NonNull Context context,
+ @NonNull ApplicationInfo applicationInfo, @StringRes int resId) {
+ if (resId == /* invalid */ 0) {
+ return null;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = applicationInfo.packageName;
+ final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId,
+ applicationInfo);
+ if (bannerDrawable == null) {
+ return null;
+ }
+
+ final boolean isImageWidthOverScreenLength =
+ bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context);
+ final boolean isImageHeightOverScreenLength =
+ bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context);
+
+ return (isImageWidthOverScreenLength || isImageHeightOverScreenLength)
+ ? null
+ : bannerDrawable;
+ }
+
+ /**
+ * Gets the width of the screen.
+ *
+ * @param context the current context.
+ * @return the width of the screen in term of pixels.
+ *
+ * @hide
+ */
+ private static int getScreenWidthPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenWidthDp = resources.getConfiguration().screenWidthDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
+ resources.getDisplayMetrics()));
+ }
+
+ /**
+ * Gets the height of the screen.
+ *
+ * @param context the current context.
+ * @return the height of the screen in term of pixels.
+ *
+ * @hide
+ */
+ private static int getScreenHeightPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenHeightDp = resources.getConfiguration().screenHeightDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
+ resources.getDisplayMetrics()));
+ }
}
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 6209679..a812f29 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -28,13 +29,19 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
/**
* Activities of interest to users with accessibility needs may request to be targets of the
@@ -87,6 +94,13 @@
*/
private final int mHtmlDescriptionRes;
+ // Used for html description of accessibility service. The <img> src tag must follow the
+ // prefix rule. e.g. <img src="R.drawable.fileName"/>
+ private static final String IMG_PREFIX = "R.drawable.";
+ private static final String ANCHOR_TAG = "a";
+ private static final List<String> UNSUPPORTED_TAG_LIST = new ArrayList<>(
+ Collections.singletonList(ANCHOR_TAG));
+
/**
* Creates a new instance.
*
@@ -134,7 +148,7 @@
// Gets animated image
mAnimatedImageRes = asAttributes.getResourceId(
com.android.internal.R.styleable
- .AccessibilityShortcutTarget_animatedImageDrawable, 0);
+ .AccessibilityShortcutTarget_animatedImageDrawable, /* defValue= */ 0);
// Gets html description
mHtmlDescriptionRes = asAttributes.getResourceId(
com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
@@ -192,7 +206,7 @@
}
/**
- * The animated image resource id of the accessibility shortcut target.
+ * Gets the animated image resource id.
*
* @return The animated image resource id.
*
@@ -205,7 +219,8 @@
/**
* The animated image drawable of the accessibility shortcut target.
*
- * @return The animated image drawable.
+ * @return The animated image drawable, or null if the resource is invalid or the image
+ * exceed the screen size.
*/
@Nullable
public Drawable loadAnimatedImage(@NonNull Context context) {
@@ -213,21 +228,20 @@
return null;
}
- final PackageManager packageManager = context.getPackageManager();
- final String packageName = mComponentName.getPackageName();
- final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo;
-
- return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ return loadSafeAnimatedImage(context, mActivityInfo.applicationInfo, mAnimatedImageRes);
}
/**
- * The localized html description of the accessibility shortcut target.
+ * The localized and restricted html description of the accessibility shortcut target.
+ * It filters the <img> tag which do not meet the custom specification and the <a> tag.
*
- * @return The localized html description.
+ * @return The localized and restricted html description.
*/
@Nullable
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
- return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes);
+ final String htmlDescription = loadResourceString(packageManager, mActivityInfo,
+ mHtmlDescriptionRes);
+ return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription);
}
/**
@@ -291,4 +305,103 @@
stringBuilder.append("]");
return stringBuilder.toString();
}
+
+ /**
+ * Gets the filtered html string for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It filters
+ * the <img> tag which do not meet the custom specification and the <a> tag.
+ *
+ * @param text the target text is html format.
+ * @return the filtered html string.
+ *
+ * @hide
+ */
+ public static @NonNull String getFilteredHtmlText(@NonNull String text) {
+ final String replacementStart = "<invalidtag ";
+ final String replacementEnd = "</invalidtag>";
+
+ for (String tag : UNSUPPORTED_TAG_LIST) {
+ final String regexStart = "(?i)<" + tag + "(\\s+|>)";
+ final String regexEnd = "(?i)</" + tag + "\\s*>";
+ text = Pattern.compile(regexStart).matcher(text).replaceAll(replacementStart);
+ text = Pattern.compile(regexEnd).matcher(text).replaceAll(replacementEnd);
+ }
+
+ final String regexInvalidImgTag = "(?i)<img\\s+(?!src\\s*=\\s*\"(?-i)" + IMG_PREFIX + ")";
+ text = Pattern.compile(regexInvalidImgTag).matcher(text).replaceAll(
+ replacementStart);
+
+ return text;
+ }
+
+ /**
+ * Loads the animated image for
+ * {@link android.accessibilityservice.AccessibilityServiceInfo} and
+ * {@link android.accessibilityservice.AccessibilityShortcutInfo}. It checks the resource
+ * whether to exceed the screen size.
+ *
+ * @param context the current context.
+ * @param applicationInfo the current application.
+ * @param resId the animated image resource id.
+ * @return the animated image which is safe.
+ *
+ * @hide
+ */
+ @Nullable
+ public static Drawable loadSafeAnimatedImage(@NonNull Context context,
+ @NonNull ApplicationInfo applicationInfo, @StringRes int resId) {
+ if (resId == /* invalid */ 0) {
+ return null;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = applicationInfo.packageName;
+ final Drawable bannerDrawable = packageManager.getDrawable(packageName, resId,
+ applicationInfo);
+ if (bannerDrawable == null) {
+ return null;
+ }
+
+ final boolean isImageWidthOverScreenLength =
+ bannerDrawable.getIntrinsicWidth() > getScreenWidthPixels(context);
+ final boolean isImageHeightOverScreenLength =
+ bannerDrawable.getIntrinsicHeight() > getScreenHeightPixels(context);
+
+ return (isImageWidthOverScreenLength || isImageHeightOverScreenLength)
+ ? null
+ : bannerDrawable;
+ }
+
+ /**
+ * Gets the width of the screen.
+ *
+ * @param context the current context.
+ * @return the width of the screen in term of pixels.
+ *
+ * @hide
+ */
+ private static int getScreenWidthPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenWidthDp = resources.getConfiguration().screenWidthDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenWidthDp,
+ resources.getDisplayMetrics()));
+ }
+
+ /**
+ * Gets the height of the screen.
+ *
+ * @param context the current context.
+ * @return the height of the screen in term of pixels.
+ *
+ * @hide
+ */
+ private static int getScreenHeightPixels(@NonNull Context context) {
+ final Resources resources = context.getResources();
+ final int screenHeightDp = resources.getConfiguration().screenHeightDp;
+
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
+ resources.getDisplayMetrics()));
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 82fdb90..b51bbdf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2012,15 +2012,16 @@
/** See {@link android.view.Surface.Rotation} */
@Surface.Rotation
private int mRotation;
+ /** The size of the snapshot before scaling */
+ private final Point mTaskSize;
private final Rect mContentInsets;
- // Whether this snapshot is a down-sampled version of the full resolution, used mainly for
- // low-ram devices
+ // Whether this snapshot is a down-sampled version of the high resolution snapshot, used
+ // mainly for loading snapshots quickly from disk when user is flinging fast
private final boolean mIsLowResolution;
// Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to
// the task having a secure window or having previews disabled
private final boolean mIsRealSnapshot;
private final int mWindowingMode;
- private final float mScale;
private final int mSystemUiVisibility;
private final boolean mIsTranslucent;
// Must be one of the named color spaces, otherwise, always use SRGB color space.
@@ -2028,9 +2029,9 @@
public TaskSnapshot(long id,
@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
- @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets,
- boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode,
- int systemUiVisibility, boolean isTranslucent) {
+ @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
+ Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
+ int windowingMode, int systemUiVisibility, boolean isTranslucent) {
mId = id;
mTopActivityComponent = topActivityComponent;
mSnapshot = snapshot;
@@ -2038,9 +2039,9 @@
? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
mOrientation = orientation;
mRotation = rotation;
+ mTaskSize = new Point(taskSize);
mContentInsets = new Rect(contentInsets);
mIsLowResolution = isLowResolution;
- mScale = scale;
mIsRealSnapshot = isRealSnapshot;
mWindowingMode = windowingMode;
mSystemUiVisibility = systemUiVisibility;
@@ -2057,9 +2058,9 @@
: ColorSpace.get(ColorSpace.Named.SRGB);
mOrientation = source.readInt();
mRotation = source.readInt();
+ mTaskSize = source.readParcelable(null /* classLoader */);
mContentInsets = source.readParcelable(null /* classLoader */);
mIsLowResolution = source.readBoolean();
- mScale = source.readFloat();
mIsRealSnapshot = source.readBoolean();
mWindowingMode = source.readInt();
mSystemUiVisibility = source.readInt();
@@ -2111,6 +2112,14 @@
}
/**
+ * @return The size of the task at the point this snapshot was taken.
+ */
+ @UnsupportedAppUsage
+ public Point getTaskSize() {
+ return mTaskSize;
+ }
+
+ /**
* @return The system/content insets on the snapshot. These can be clipped off in order to
* remove any areas behind system bars in the snapshot.
*/
@@ -2159,14 +2168,6 @@
return mSystemUiVisibility;
}
- /**
- * @return The scale this snapshot was taken in.
- */
- @UnsupportedAppUsage
- public float getScale() {
- return mScale;
- }
-
@Override
public int describeContents() {
return 0;
@@ -2180,9 +2181,9 @@
dest.writeInt(mColorSpace.getId());
dest.writeInt(mOrientation);
dest.writeInt(mRotation);
+ dest.writeParcelable(mTaskSize, 0);
dest.writeParcelable(mContentInsets, 0);
dest.writeBoolean(mIsLowResolution);
- dest.writeFloat(mScale);
dest.writeBoolean(mIsRealSnapshot);
dest.writeInt(mWindowingMode);
dest.writeInt(mSystemUiVisibility);
@@ -2200,9 +2201,11 @@
+ " mColorSpace=" + mColorSpace.toString()
+ " mOrientation=" + mOrientation
+ " mRotation=" + mRotation
+ + " mTaskSize=" + mTaskSize.toString()
+ " mContentInsets=" + mContentInsets.toShortString()
- + " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale
- + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode
+ + " mIsLowResolution=" + mIsLowResolution
+ + " mIsRealSnapshot=" + mIsRealSnapshot
+ + " mWindowingMode=" + mWindowingMode
+ " mSystemUiVisibility=" + mSystemUiVisibility
+ " mIsTranslucent=" + mIsTranslucent;
}
@@ -2224,9 +2227,8 @@
private ColorSpace mColorSpace;
private int mOrientation;
private int mRotation;
+ private Point mTaskSize;
private Rect mContentInsets;
- private boolean mIsLowResolution;
- private float mScaleFraction;
private boolean mIsRealSnapshot;
private int mWindowingMode;
private int mSystemUiVisibility;
@@ -2263,28 +2265,19 @@
return this;
}
+ /**
+ * Sets the original size of the task
+ */
+ public Builder setTaskSize(Point size) {
+ mTaskSize = size;
+ return this;
+ }
+
public Builder setContentInsets(Rect contentInsets) {
mContentInsets = contentInsets;
return this;
}
- /**
- * Set to true if this is a low-resolution snapshot stored in *_reduced.jpg.
- */
- public Builder setIsLowResolution(boolean isLowResolution) {
- mIsLowResolution = isLowResolution;
- return this;
- }
-
- public float getScaleFraction() {
- return mScaleFraction;
- }
-
- public Builder setScaleFraction(float scaleFraction) {
- mScaleFraction = scaleFraction;
- return this;
- }
-
public Builder setIsRealSnapshot(boolean realSnapshot) {
mIsRealSnapshot = realSnapshot;
return this;
@@ -2322,9 +2315,12 @@
mColorSpace,
mOrientation,
mRotation,
+ mTaskSize,
mContentInsets,
- mIsLowResolution,
- mScaleFraction,
+ // When building a TaskSnapshot with the Builder class, isLowResolution
+ // is always false. Low-res snapshots are only created when loading from
+ // disk.
+ false /* isLowResolution */,
mIsRealSnapshot,
mWindowingMode,
mSystemUiVisibility,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8f02f15..a53fc35 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6910,11 +6910,7 @@
* Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
*/
public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
- try {
- return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return unsafeCheckOpRawNoThrow(op, uid, packageName);
}
/**
@@ -6923,8 +6919,17 @@
* {@link #MODE_FOREGROUND}.
*/
public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
+ return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
+ }
+
+ /**
+ * Returns the <em>raw</em> mode associated with the op.
+ * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
+ * @hide
+ */
+ public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
try {
- return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
+ return mService.checkOperationRaw(op, uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index f0eedf3..7f43640 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -260,6 +260,7 @@
Log.e(TAG, "Failed to notify app target event", e);
e.rethrowAsRuntimeException();
}
+ mRegisteredCallbacks.clear();
} else {
throw new IllegalStateException("This client has already been destroyed.");
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6dea1c6..ccd8199 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -182,6 +182,16 @@
public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
/**
+ * A bundle extra that contains whether or not an app has finished restoring a widget.
+ * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its
+ * widgets followed by calling {@link #updateAppWidget} to update the views.
+ *
+ * @see #updateAppWidgetOptions(int, Bundle)
+ */
+ public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
+
+
+ /**
* A bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
*/
public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index ab91edf..a5d2198 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -200,6 +200,9 @@
* provider can immediately generate new RemoteViews suitable for its newly-restored set
* of instances.
*
+ * <p>In addition, you should set {@link AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED}
+ * to true indicate if a widget has been restored successfully from the provider's side.
+ *
* {@more}
*
* @param context
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 9e40f46..9da0f20 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -54,7 +54,6 @@
private final int mHash;
- @UnsupportedAppUsage
public ResourcesKey(@Nullable String resDir,
@Nullable String[] splitResDirs,
@Nullable String[] overlayDirs,
@@ -85,6 +84,18 @@
mHash = hash;
}
+ @UnsupportedAppUsage
+ public ResourcesKey(@Nullable String resDir,
+ @Nullable String[] splitResDirs,
+ @Nullable String[] overlayDirs,
+ @Nullable String[] libDirs,
+ int displayId,
+ @Nullable Configuration overrideConfig,
+ @Nullable CompatibilityInfo compatInfo) {
+ this(resDir, splitResDirs, overlayDirs, libDirs, displayId, overrideConfig, compatInfo,
+ null);
+ }
+
public boolean hasOverrideConfiguration() {
return !Configuration.EMPTY.equals(mOverrideConfiguration);
}
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
new file mode 100644
index 0000000..2e94be5
--- /dev/null
+++ b/core/java/android/service/notification/OWNERS
@@ -0,0 +1,4 @@
+juliacr@google.com
+beverlyt@google.com
+dsandler@android.com
+pixel@google.com
\ No newline at end of file
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 7b24ba9..9a555c16 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -32,7 +32,7 @@
* <p>
* This API is not as convenient or type safe as the standard protobuf
* classes. If possible, the best recommended library is to use protobuf lite.
- * However, in environements (such as the Android platform itself), a
+ * However, in environments (such as the Android platform itself), a
* more memory efficient version is necessary.
*
* <p>Each write method takes an ID code from the protoc generated classes
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 5f9c480..cb9e746 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -233,6 +233,21 @@
return mSeq;
}
+ /**
+ * Gets the ID of this event. This is generated when an event is created and preserved until its
+ * last stage. It won't change just because the event crosses process boundary, but should
+ * change when making a copy with modifications.
+ * <p>
+ * To avoid exposing app usage to other processes this ID is generated from a CSPRNG. Therefore
+ * there isn't 100% guarantee on the uniqueness of this ID, though the chance of ID collisions
+ * is considerably low. The rule of thumb is not to rely on the uniqueness for production logic,
+ * but a good source for tracking an event (e.g. logging and profiling).
+ *
+ * @return The ID of this event.
+ * @hide
+ */
+ public abstract int getId();
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 235f5e1..e249c77 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1265,6 +1265,7 @@
private KeyEvent mNext;
+ private int mId;
@UnsupportedAppUsage
private int mDeviceId;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -1350,9 +1351,9 @@
private static native String nativeKeyCodeToString(int keyCode);
private static native int nativeKeyCodeFromString(String keyCode);
+ private static native int nativeNextId();
- private KeyEvent() {
- }
+ private KeyEvent() {}
/**
* Create a new key event.
@@ -1362,6 +1363,7 @@
* @param code The key code.
*/
public KeyEvent(int action, int code) {
+ mId = nativeNextId();
mAction = action;
mKeyCode = code;
mRepeatCount = 0;
@@ -1383,6 +1385,7 @@
*/
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1407,6 +1410,7 @@
*/
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1435,6 +1439,7 @@
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1465,6 +1470,7 @@
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1497,6 +1503,7 @@
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source) {
+ mId = nativeNextId();
mDownTime = downTime;
mEventTime = eventTime;
mAction = action;
@@ -1523,6 +1530,7 @@
* @param flags The flags for this key event
*/
public KeyEvent(long time, String characters, int deviceId, int flags) {
+ mId = nativeNextId();
mDownTime = time;
mEventTime = time;
mCharacters = characters;
@@ -1539,6 +1547,7 @@
* Make an exact copy of an existing key event.
*/
public KeyEvent(KeyEvent origEvent) {
+ mId = origEvent.mId;
mDownTime = origEvent.mDownTime;
mEventTime = origEvent.mEventTime;
mAction = origEvent.mAction;
@@ -1567,6 +1576,7 @@
*/
@Deprecated
public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
+ mId = nativeNextId(); // Not an exact copy so assign a new ID.
mDownTime = origEvent.mDownTime;
mEventTime = eventTime;
mAction = origEvent.mAction;
@@ -1598,15 +1608,16 @@
}
/**
- * Obtains a (potentially recycled) key event.
+ * Obtains a (potentially recycled) key event. Used by native code to create a Java object.
*
* @hide
*/
- public static KeyEvent obtain(long downTime, long eventTime, int action,
+ public static KeyEvent obtain(int id, long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac,
String characters) {
KeyEvent ev = obtain();
+ ev.mId = id;
ev.mDownTime = downTime;
ev.mEventTime = eventTime;
ev.mAction = action;
@@ -1628,12 +1639,24 @@
*
* @hide
*/
+ public static KeyEvent obtain(long downTime, long eventTime, int action,
+ int code, int repeat, int metaState,
+ int deviceId, int scanCode, int flags, int source, int displayId, String characters) {
+ return obtain(nativeNextId(), downTime, eventTime, action, code, repeat, metaState,
+ deviceId, scanCode, flags, source, displayId, null /* hmac */, characters);
+ }
+
+ /**
+ * Obtains a (potentially recycled) key event.
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public static KeyEvent obtain(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, String characters) {
return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
- flags, source, INVALID_DISPLAY, null /* hmac */, characters);
+ flags, source, INVALID_DISPLAY, characters);
}
/**
@@ -1645,6 +1668,7 @@
*/
public static KeyEvent obtain(KeyEvent other) {
KeyEvent ev = obtain();
+ ev.mId = other.mId;
ev.mDownTime = other.mDownTime;
ev.mEventTime = other.mEventTime;
ev.mAction = other.mAction;
@@ -1695,6 +1719,12 @@
// Do nothing.
}
+ /** @hide */
+ @Override
+ public int getId() {
+ return mId;
+ }
+
/**
* Create a new key event that is the same as the given one, but whose
* event time and repeat count are replaced with the given value.
@@ -1723,6 +1753,7 @@
public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime,
int newRepeat, int newFlags) {
KeyEvent ret = new KeyEvent(event);
+ ret.mId = nativeNextId(); // Not an exact copy so assign a new ID.
ret.mEventTime = eventTime;
ret.mRepeatCount = newRepeat;
ret.mFlags = newFlags;
@@ -1736,6 +1767,7 @@
* @param action The new action code of the event.
*/
private KeyEvent(KeyEvent origEvent, int action) {
+ mId = nativeNextId(); // Not an exact copy so assign a new ID.
mDownTime = origEvent.mDownTime;
mEventTime = origEvent.mEventTime;
mAction = action;
@@ -1772,6 +1804,7 @@
*/
public static KeyEvent changeFlags(KeyEvent event, int flags) {
event = new KeyEvent(event);
+ event.mId = nativeNextId(); // Not an exact copy so assign a new ID.
event.mFlags = flags;
return event;
}
@@ -3095,6 +3128,7 @@
}
private KeyEvent(Parcel in) {
+ mId = in.readInt();
mDeviceId = in.readInt();
mSource = in.readInt();
mDisplayId = in.readInt();
@@ -3114,6 +3148,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_KEY_EVENT);
+ out.writeInt(mId);
out.writeInt(mDeviceId);
out.writeInt(mSource);
out.writeInt(mDisplayId);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0068476..19eff72 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1550,6 +1550,8 @@
private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
boolean keepHistory);
@CriticalNative
+ private static native int nativeGetId(long nativePtr);
+ @CriticalNative
private static native int nativeGetDeviceId(long nativePtr);
@CriticalNative
private static native int nativeGetSource(long nativePtr);
@@ -2024,6 +2026,12 @@
}
}
+ /** @hide */
+ @Override
+ public int getId() {
+ return nativeGetId(mNativePtr);
+ }
+
/** {@inheritDoc} */
@Override
public final int getDeviceId() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4a093e6..918b5a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5379,7 +5379,8 @@
if (mTracePrefix == null) {
mTracePrefix = getClass().getSimpleName();
}
- Trace.traceBegin(traceTag, mTracePrefix + " seq#=" + q.mEvent.getSequenceNumber());
+ Trace.traceBegin(traceTag, mTracePrefix + " id=0x"
+ + Integer.toHexString(q.mEvent.getId()));
}
}
@@ -7986,12 +7987,13 @@
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
- q.mEvent.getSequenceNumber());
+ q.mEvent.getId());
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x"
+ Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano="
- + q.mEvent.getEventTimeNano() + " seq#=" + q.mEvent.getSequenceNumber());
+ + q.mEvent.getEventTimeNano() + " id=0x"
+ + Integer.toHexString(q.mEvent.getId()));
}
try {
if (mInputEventConsistencyVerifier != null) {
@@ -8032,7 +8034,7 @@
private void finishInputEvent(QueuedInputEvent q) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
- q.mEvent.getSequenceNumber());
+ q.mEvent.getId());
if (q.mReceiver != null) {
boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 84acf9a..0a2b1d4 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -114,9 +114,9 @@
uint32_t publishedSeq = mNextPublishedSeq++;
status_t status =
- mInputPublisher.publishKeyEvent(publishedSeq, event->getDeviceId(), event->getSource(),
- event->getDisplayId(), event->getHmac(),
- event->getAction(), event->getFlags(),
+ mInputPublisher.publishKeyEvent(publishedSeq, event->getId(), event->getDeviceId(),
+ event->getSource(), event->getDisplayId(),
+ event->getHmac(), event->getAction(), event->getFlags(),
event->getKeyCode(), event->getScanCode(),
event->getMetaState(), event->getRepeatCount(),
event->getDownTime(), event->getEventTime());
@@ -138,12 +138,12 @@
for (size_t i = 0; i <= event->getHistorySize(); i++) {
publishedSeq = mNextPublishedSeq++;
status_t status =
- mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(),
- event->getSource(), event->getDisplayId(),
- event->getHmac(), event->getAction(),
- event->getActionButton(), event->getFlags(),
- event->getEdgeFlags(), event->getMetaState(),
- event->getButtonState(),
+ mInputPublisher.publishMotionEvent(publishedSeq, event->getId(),
+ event->getDeviceId(), event->getSource(),
+ event->getDisplayId(), event->getHmac(),
+ event->getAction(), event->getActionButton(),
+ event->getFlags(), event->getEdgeFlags(),
+ event->getMetaState(), event->getButtonState(),
event->getClassification(), event->getXScale(),
event->getYScale(), event->getXOffset(),
event->getYOffset(), event->getXPrecision(),
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index bbe563e..54567e5 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -75,6 +75,7 @@
jmethodID obtain;
jmethodID recycle;
+ jfieldID mId;
jfieldID mDeviceId;
jfieldID mSource;
jfieldID mDisplayId;
@@ -96,6 +97,7 @@
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
jobject eventObj =
env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
+ event->getId(),
nanoseconds_to_milliseconds(event->getDownTime()),
nanoseconds_to_milliseconds(event->getEventTime()),
event->getAction(), event->getKeyCode(),
@@ -114,6 +116,7 @@
status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
KeyEvent* event) {
+ jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId);
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId);
@@ -131,7 +134,7 @@
jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
- event->initialize(deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
+ event->initialize(id, deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
metaState, repeatCount, milliseconds_to_nanoseconds(downTime),
milliseconds_to_nanoseconds(eventTime));
return OK;
@@ -159,14 +162,18 @@
return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str());
}
+static jint android_view_KeyEvent_nativeNextId() {
+ return static_cast<jint>(InputEvent::nextId());
+}
// ----------------------------------------------------------------------------
static const JNINativeMethod g_methods[] = {
- { "nativeKeyCodeToString", "(I)Ljava/lang/String;",
- (void*)android_view_KeyEvent_nativeKeyCodeToString},
- { "nativeKeyCodeFromString", "(Ljava/lang/String;)I",
- (void*)android_view_KeyEvent_nativeKeyCodeFromString},
+ {"nativeKeyCodeToString", "(I)Ljava/lang/String;",
+ (void*)android_view_KeyEvent_nativeKeyCodeToString},
+ {"nativeKeyCodeFromString", "(Ljava/lang/String;)I",
+ (void*)android_view_KeyEvent_nativeKeyCodeFromString},
+ {"nativeNextId", "()I", (void*)android_view_KeyEvent_nativeNextId},
};
int register_android_view_KeyEvent(JNIEnv* env) {
@@ -175,10 +182,11 @@
gKeyEventClassInfo.obtain =
GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz, "obtain",
- "(JJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
+ "(IJJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
"recycle", "()V");
+ gKeyEventClassInfo.mId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mId", "I");
gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I");
gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 3335fb2..9816d71 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -369,9 +369,10 @@
env->DeleteLocalRef(pointerCoordsObj);
}
- event->initialize(deviceId, source, displayId, INVALID_HMAC, action, 0, flags, edgeFlags,
- metaState, buttonState, static_cast<MotionClassification>(classification),
- 1 /*xScale*/, 1 /*yScale*/, xOffset, yOffset, xPrecision, yPrecision,
+ event->initialize(InputEvent::nextId(), deviceId, source, displayId, INVALID_HMAC, action, 0,
+ flags, edgeFlags, metaState, buttonState,
+ static_cast<MotionClassification>(classification), 1 /*xScale*/, 1 /*yScale*/,
+ xOffset, yOffset, xPrecision, yPrecision,
AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
downTimeNanos, eventTimeNanos, pointerCount, pointerProperties,
rawPointerCoords);
@@ -592,6 +593,11 @@
return reinterpret_cast<jlong>(destEvent);
}
+static jint android_view_MotionEvent_nativeGetId(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return event->getId();
+}
+
static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
return event->getDeviceId();
@@ -790,6 +796,7 @@
// --------------- @CriticalNative ------------------
{"nativeCopy", "(JJZ)J", (void*)android_view_MotionEvent_nativeCopy},
+ {"nativeGetId", "(J)I", (void*)android_view_MotionEvent_nativeGetId},
{"nativeGetDeviceId", "(J)I", (void*)android_view_MotionEvent_nativeGetDeviceId},
{"nativeGetSource", "(J)I", (void*)android_view_MotionEvent_nativeGetSource},
{"nativeSetSource", "(JI)V", (void*)android_view_MotionEvent_nativeSetSource},
diff --git a/core/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto
index cd7173a..97350ef 100644
--- a/core/proto/android/service/appwidget.proto
+++ b/core/proto/android/service/appwidget.proto
@@ -36,4 +36,5 @@
optional int32 minHeight = 7;
optional int32 maxWidth = 8;
optional int32 maxHeight = 9;
+ optional bool restoreCompleted = 10;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 490892e..8023990 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2752,7 +2752,9 @@
<!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
Reduced scale snapshots are loaded before full screen snapshots to improve load times and
- minimize the chance the user will see an empty task card. -->
+ minimize the chance the user will see an empty task card. If set to 0, reduced scale
+ snapshots are disabled, and snapshots will only be stored at config_highResTaskSnapshotScale
+ -->
<item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item>
<!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
@@ -3957,14 +3959,14 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
- <!-- This is the default launcher component to use on secondary displays that support system
- decorations.
- This launcher activity must support multiple instances and have corresponding launch mode
- set in AndroidManifest.
+ <!-- This is the default launcher package with an activity to use on secondary displays that
+ support system decorations.
+ This launcher package must have an activity that supports multiple instances and has
+ corresponding launch mode set in AndroidManifest.
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
- <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
+ <string name="config_secondaryHomePackage" translatable="false">com.android.launcher3</string>
- <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is
+ <!-- Force secondary home launcher specified in config_secondaryHomePackage always. If this is
not set, secondary home launcher can be replaced by user. -->
<bool name ="config_useSystemProvidedLauncherForSecondary">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7690b94..3ed3a64e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3678,7 +3678,7 @@
<java-symbol type="string" name="config_defaultModuleMetadataProvider" />
<!-- For Secondary Launcher -->
- <java-symbol type="string" name="config_secondaryHomeComponent" />
+ <java-symbol type="string" name="config_secondaryHomePackage" />
<java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" />
<java-symbol type="string" name="battery_saver_notification_channel_name" />
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index 14999c7..623008e 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -19,6 +19,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import android.os.Parcel;
@@ -28,10 +29,14 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyEventTest {
+ private static final int ID = 0xabcdef;
private static final int DOWN_TIME = 50;
private static final long EVENT_TIME = 100;
private static final int ACTION = KeyEvent.ACTION_DOWN;
@@ -45,6 +50,8 @@
private static final byte[] HMAC = null;
private static final String CHARACTERS = null;
+ private static final int ID_SOURCE_MASK = 0x3 << 30;
+
@Test
public void testObtain() {
KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
@@ -75,8 +82,7 @@
public void testObtainWithDisplayId() {
final int displayId = 5;
KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
- METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, null /* hmac*/,
- CHARACTERS);
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS);
assertEquals(DOWN_TIME, keyEvent.getDownTime());
assertEquals(EVENT_TIME, keyEvent.getEventTime());
assertEquals(ACTION, keyEvent.getAction());
@@ -91,6 +97,52 @@
assertEquals(CHARACTERS, keyEvent.getCharacters());
}
+ /**
+ * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test
+ * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough.
+ * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for
+ * this test.
+ */
+ @Test
+ public void testObtainGeneratesUniqueId() {
+ Set<Integer> set = new HashSet<>();
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+ assertFalse("Found duplicate ID in round " + i,
+ set.contains(keyEvent.getId()));
+ set.add(keyEvent.getSequenceNumber());
+ }
+ }
+
+ @Test
+ public void testConstructorGeneratesUniqueId() {
+ Set<Integer> set = new HashSet<>();
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT);
+ assertFalse("Found duplicate sequence number in round " + i,
+ set.contains(keyEvent.getId()));
+ set.add(keyEvent.getSequenceNumber());
+ }
+ }
+
+ @Test
+ public void testObtainGeneratesIdWithRightSource() {
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+ METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+ assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId());
+ }
+ }
+
+ @Test
+ public void testConstructorGeneratesIdWithRightSource() {
+ for (int i = 0; i < 500; ++i) {
+ KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT);
+ assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId());
+ }
+ }
+
@Test
public void testParcelUnparcel() {
KeyEvent key1 = createKey();
@@ -112,11 +164,12 @@
}
private static KeyEvent createKey() {
- return KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
- METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
+ return KeyEvent.obtain(ID, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
+ DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
}
private static void compareKeys(KeyEvent key1, KeyEvent key2) {
+ assertEquals(key1.getId(), key2.getId());
assertEquals(key1.getDownTime(), key2.getDownTime());
assertEquals(key1.getEventTime(), key2.getEventTime());
assertEquals(key1.getAction(), key2.getAction());
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 9d09830..786ae89 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -23,6 +23,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import android.view.MotionEvent.PointerCoords;
@@ -34,9 +35,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashSet;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MotionEventTest {
+ private static final int ID_SOURCE_MASK = 0x3 << 30;
@Test
public void testObtainWithDisplayId() {
@@ -138,4 +143,30 @@
assertEquals(30, event.getXCursorPosition(), 0.1);
assertEquals(50, event.getYCursorPosition(), 0.1);
}
+
+ /**
+ * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test
+ * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough.
+ * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for
+ * this test.
+ */
+ @Test
+ public void testObtainGeneratesUniqueId() {
+ Set<Integer> set = new HashSet<>();
+ for (int i = 0; i < 500; ++i) {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+ assertFalse("Found duplicate ID in round " + i, set.contains(event.getId()));
+ set.add(event.getSequenceNumber());
+ }
+ }
+
+ @Test
+ public void testObtainGeneratesIdWithRightSource() {
+ for (int i = 0; i < 500; ++i) {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+ assertEquals(0x3 << 30, ID_SOURCE_MASK & event.getId());
+ }
+ }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 145fd8b..99605ad 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -697,6 +697,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-668956537": {
+ "message": " THUMBNAIL %s: CREATE",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/SurfaceFreezer.java"
+ },
"-666510420": {
"message": "With display frozen, orientationChangeComplete=%b",
"level": "VERBOSE",
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f2b4db1..f800f9e 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2531,7 +2531,7 @@
int offset, int size, long presentationTimeUs, int flags)
throws CryptoException {
synchronized(mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
invalidateByteBuffer(mCachedInputBuffers, index);
@@ -2783,7 +2783,7 @@
long presentationTimeUs,
int flags) throws CryptoException {
synchronized(mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
invalidateByteBuffer(mCachedInputBuffers, index);
@@ -2818,7 +2818,7 @@
*/
public final int dequeueInputBuffer(long timeoutUs) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -3534,7 +3534,7 @@
public final int dequeueOutputBuffer(
@NonNull BufferInfo info, long timeoutUs) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -3916,7 +3916,7 @@
@NonNull
public ByteBuffer[] getInputBuffers() {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
if (mCachedInputBuffers == null) {
@@ -3952,7 +3952,7 @@
@NonNull
public ByteBuffer[] getOutputBuffers() {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
if (mCachedOutputBuffers == null) {
@@ -3984,7 +3984,7 @@
@Nullable
public ByteBuffer getInputBuffer(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -4018,7 +4018,7 @@
@Nullable
public Image getInputImage(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -4052,7 +4052,7 @@
@Nullable
public ByteBuffer getOutputBuffer(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
@@ -4085,7 +4085,7 @@
@Nullable
public Image getOutputImage(int index) {
synchronized (mBufferLock) {
- if (mBufferMode != BUFFER_MODE_LEGACY) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException();
}
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index b579144..95199cc 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -240,7 +240,7 @@
* @param inputId The ID of the TV input associated with the session.
*/
@Nullable
- public abstract Session onCreateSession(String inputId);
+ public abstract Session onCreateSession(@NonNull String inputId);
/**
* Returns a concrete implementation of {@link RecordingSession}.
@@ -251,7 +251,7 @@
* @param inputId The ID of the TV input associated with the recording session.
*/
@Nullable
- public RecordingSession onCreateRecordingSession(String inputId) {
+ public RecordingSession onCreateRecordingSession(@NonNull String inputId) {
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 3f55cea..8bf48e59 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -68,6 +68,7 @@
private WifiInfo mWifiInfo;
public boolean enabled;
+ public boolean isCaptivePortal;
public int state;
public boolean connected;
public String ssid;
@@ -155,9 +156,11 @@
private void updateStatusLabel() {
final NetworkCapabilities networkCapabilities
= mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork());
+ isCaptivePortal = false;
if (networkCapabilities != null) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
statusLabel = mContext.getString(R.string.wifi_status_sign_in_required);
+ isCaptivePortal = true;
return;
} else if (networkCapabilities.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
statusLabel = mContext.getString(R.string.wifi_limited_connection);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 0f2ee6a..610165a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -268,6 +268,7 @@
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
diff --git a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
index 7964609..015e9f9 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_button_bg_stroke.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
- android:color="@color/notification_guts_priority_button_bg_stroke_color_selected" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@color/notification_guts_priority_button_bg_stroke_color" />
</selector>
diff --git a/packages/SystemUI/res/color/notification_guts_priority_contents.xml b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
index 56c43f0..42f0189 100644
--- a/packages/SystemUI/res/color/notification_guts_priority_contents.xml
+++ b/packages/SystemUI/res/color/notification_guts_priority_contents.xml
@@ -16,6 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
- android:color="@color/notification_guts_priority_button_content_color_selected" />
+ android:color="?android:attr/colorAccent" />
<item android:color="@color/notification_guts_priority_button_content_color" />
</selector>
diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml
index 1730dce..2fe6c7b 100644
--- a/packages/SystemUI/res/drawable/notification_guts_bg.xml
+++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml
@@ -16,7 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/notification_guts_bg_color" />
+ <solid android:color="@color/notification_material_background_color" />
<!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners -->
<corners android:radius="1dp" />
</shape>
diff --git a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
new file mode 100644
index 0000000..e3d010e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape>
+ <solid android:color="@color/qs_user_detail_avatar_frame" />
+
+ <padding
+ android:left="1dp"
+ android:right="1dp"
+ android:bottom="1dp"
+ android:top="1dp" />
+
+ <corners android:radius="48dp" />
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
new file mode 100644
index 0000000..1f38b1e
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<!-- LinearLayout -->
+<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp"
+ android:layout_marginEnd="8dp"
+ android:gravity="end|center_vertical"
+ android:clickable="true"
+ android:background="@drawable/rounded_user_switcher_bg"
+ sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+ sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
+ <TextView android:id="@+id/user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="13dp"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+ />
+ <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
+ android:layout_width="@dimen/framed_avatar_size"
+ android:layout_height="@dimen/framed_avatar_size"
+ android:contentDescription="@null"
+ sysui:badgeDiameter="18dp"
+ sysui:badgeMargin="1dp" />
+</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml
index 82a0115..bfa252c 100644
--- a/packages/SystemUI/res/layout/app_ops_info.xml
+++ b/packages/SystemUI/res/layout/app_ops_info.xml
@@ -26,7 +26,7 @@
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
android:paddingEnd="@*android:dimen/notification_content_margin_end"
- android:background="@color/notification_guts_bg_color"
+ android:background="@color/notification_material_background_color"
android:theme="@*android:style/Theme.DeviceDefault.Light">
<!-- Package Info -->
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index fb38e1c..b1e5165 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -27,8 +27,6 @@
android:gravity="center_vertical"
android:clickable="true"
android:background="@drawable/ripple_drawable"
- android:clipChildren="false"
- android:clipToPadding="false"
sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
<TextView android:id="@+id/user_name"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 87de9d4..5d03eee 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -24,8 +24,7 @@
android:clipChildren="false"
android:clipToPadding="true"
android:orientation="vertical"
- android:paddingStart="@*android:dimen/notification_content_margin_start"
- android:background="@color/notification_guts_bg_color">
+ android:paddingStart="@*android:dimen/notification_content_margin_start">
<!-- Package Info -->
<RelativeLayout
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index ffe2eee..c350ed2 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -21,7 +21,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true"
- android:background="@color/notification_guts_bg_color"
+ android:background="@color/notification_material_background_color"
android:theme="@style/Theme.SystemUI">
<RelativeLayout
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 9a66e8b..9b0fe46 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -39,23 +39,16 @@
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#30ffffff</color>
- <!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@color/GM2_grey_900</color>
-
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
- <color name="notification_guts_info_button_color">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_500</color>
- <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">@color/GM2_grey_800</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_300</color>
-
<color name="notification_section_header_label_color">@color/GM2_grey_200</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_500</color>
<color name="notification_channel_dialog_separator">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 23b64e3..b375364 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -22,4 +22,28 @@
<style name="UserDetailView">
<item name="numColumns">4</item>
</style>
+
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
+ <item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?attr/wallpaperTextColor</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
+ <item name="android:fontWeight">700</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+ </style>
+
+ <style name="TextAppearance.QS.UserSwitcher">
+ <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ </style>
+
+ <style name="TextAppearance.QS.UserSwitcher.Activated">
+ <item name="android:fontWeight">700</item>
+ <item name="android:textStyle">bold</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 43ceb4e..73e49ce 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -88,21 +88,16 @@
<!-- The color of the text inside a notification -->
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color>
- <!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@color/GM2_grey_50</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_700</color>
<color name="notification_guts_sub_text_color">@color/GM2_grey_700</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_900</color>
<color name="notification_silence_color">#FF32c1de</color>
<color name="notification_alert_color">#FFF87B2B</color>
- <color name="notification_guts_info_button_color">@color/GM2_blue_700</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_content_color_selected">@color/GM2_blue_700</color>
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">#FFFFFF</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_300</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_600</color>
<color name="notification_section_header_label_color">@color/GM2_grey_900</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8de2df5..a49aedf 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -464,6 +464,9 @@
<!-- Allow dragging the PIP to a location to close it -->
<bool name="config_pipEnableDismissDragToEdge">true</bool>
+ <!-- Alow PIP to resize to a slightly bigger state upon touch/showing the menu -->
+ <bool name="config_pipEnableResizeForMenu">true</bool>
+
<!-- SystemUI Plugins that can be loaded on user builds. -->
<string-array name="config_pluginWhitelist" translatable="false">
<item>com.android.systemui</item>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 36c4526..d9b1452 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -514,7 +514,7 @@
<style name="TextAppearance.NotificationInfo.Button">
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/notification_guts_info_button_color</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:background">@drawable/btn_borderless_rect</item>
<item name="android:gravity">center_vertical</item>
<item name="android:focusable">true</item>
@@ -550,7 +550,7 @@
<style name="TextAppearance.NotificationImportanceButton">
<item name="android:textSize">@dimen/notification_importance_button_text</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textColor">@color/notification_guts_priority_contents</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
<item name="android:gravity">center</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 4474a49..eca6ebf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -62,7 +62,9 @@
orientation = snapshot.getOrientation();
rotation = snapshot.getRotation();
reducedResolution = snapshot.isLowResolution();
- scale = snapshot.getScale();
+ // TODO(b/149579527): Pass task size instead of computing scale.
+ // Assume width and height were scaled the same; compute scale only for width
+ scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x;
isRealSnapshot = snapshot.isRealSnapshot();
isTranslucent = snapshot.isTranslucent();
windowingMode = snapshot.getWindowingMode();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 79a25b2..3b855db 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -67,6 +67,8 @@
// Allow dragging the PIP to a location to close it
private final boolean mEnableDismissDragToEdge;
+ // Allow PIP to resize to a slightly bigger state upon touch
+ private final boolean mEnableResize;
private final Context mContext;
private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
@@ -188,6 +190,7 @@
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+ mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
// Register the listener for input consumer touch events
inputConsumerController.setInputListener(this::handleTouchEvent);
@@ -297,7 +300,7 @@
} else {
final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
* mContext.getResources().getDisplayMetrics().density;
- final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
+ final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu()
? new Rect(expandedMovementBounds)
: new Rect(normalMovementBounds);
final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
@@ -691,11 +694,12 @@
};
/**
- * Updates the current movement bounds based on whether the menu is currently visible.
+ * Updates the current movement bounds based on whether the menu is currently visible and
+ * resized.
*/
private void updateMovementBounds(int menuState) {
boolean isMenuExpanded = menuState == MENU_STATE_FULL;
- mMovementBounds = isMenuExpanded
+ mMovementBounds = isMenuExpanded && willResizeMenu()
? mExpandedMovementBounds
: mNormalMovementBounds;
mPipBoundsHandler.setMinEdgeSize(
@@ -715,8 +719,11 @@
* @return whether the menu will resize as a part of showing the full menu.
*/
private boolean willResizeMenu() {
- return mExpandedBounds.width() != mNormalBounds.width() ||
- mExpandedBounds.height() != mNormalBounds.height();
+ if (!mEnableResize) {
+ return false;
+ }
+ return mExpandedBounds.width() != mNormalBounds.width()
+ || mExpandedBounds.height() != mNormalBounds.height();
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 39bfd5a..f169501 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -206,7 +206,7 @@
state.icon = ResourceIcon.get(cb.wifiSignalIconId);
state.label = removeDoubleQuotes(cb.ssid);
} else if (wifiNotConnected) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected);
+ state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
state.label = r.getString(R.string.quick_settings_wifi_label);
} else {
state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 97755fc..004b56b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -807,7 +807,7 @@
// TODO: Move inflation logic out of this call
if (mIsChildInGroup != isChildInGroup) {
mIsChildInGroup = isChildInGroup;
- if (mIsLowPriority) {
+ if (!isRemoved() && mIsLowPriority) {
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setUseLowPriority(mIsLowPriority);
mRowContentBindStage.requestRebind(mEntry, null /* callback */);
@@ -1576,13 +1576,15 @@
// TODO: Move inflation logic out of this call and remove this method
if (mNeedsRedaction != needsRedaction) {
mNeedsRedaction = needsRedaction;
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- if (needsRedaction) {
- params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
- } else {
- params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ if (!isRemoved()) {
+ RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+ if (needsRedaction) {
+ params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ } else {
+ params.freeContentViews(FLAG_CONTENT_VIEW_PUBLIC);
+ }
+ mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 60eda06..bab7840 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -20,6 +20,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS;
@@ -218,7 +219,7 @@
// TODO: consider querying this earlier in the notification pipeline and passing it in
LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
.setPackage(mPackageName)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+ .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED)
.setShortcutIds(Arrays.asList(mConversationId));
List<ShortcutInfo> shortcuts = mLauncherApps.getShortcuts(query, mSbn.getUser());
if (shortcuts != null && !shortcuts.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index d090404..7d532a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -56,8 +56,9 @@
private int mHotspotState;
private volatile int mNumConnectedDevices;
- private volatile boolean mIsTetheringSupported;
- private volatile boolean mHasTetherableWifiRegexs;
+ // Assume tethering is available until told otherwise
+ private volatile boolean mIsTetheringSupported = true;
+ private volatile boolean mHasTetherableWifiRegexs = true;
private boolean mWaitingForTerminalState;
private TetheringManager.TetheringEventCallback mTetheringCallback =
@@ -97,6 +98,15 @@
new HandlerExecutor(backgroundHandler), mTetheringCallback);
}
+ /**
+ * Whether hotspot is currently supported.
+ *
+ * This will return {@code true} immediately on creation of the controller, but may be updated
+ * later. Callbacks from this controllers will notify if the state changes.
+ *
+ * @return {@code true} if hotspot is supported (or we haven't been told it's not)
+ * @see #addCallback
+ */
@Override
public boolean isHotspotSupported() {
return mIsTetheringSupported && mHasTetherableWifiRegexs
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 8bd0f2c..7c96386 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -88,15 +88,16 @@
boolean wifiVisible = mCurrentState.enabled
&& ((mCurrentState.connected && mCurrentState.inetCondition == 1)
|| !mHasMobileData || visibleWhenEnabled);
- String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
+ String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
String contentDescription = getTextIfExists(getContentDescription()).toString();
if (mCurrentState.inetCondition == 0) {
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
- IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
- contentDescription);
+ IconState qsIcon = new IconState(mCurrentState.connected,
+ mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
+ : getQsCurrentIconId(), contentDescription);
callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index cd91f22..5771472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -140,13 +139,16 @@
}
@Test
- public void testDefault_hotspotNotSupported() {
- assertFalse(mController.isHotspotSupported());
+ public void testHotspotSupported_default() {
+ assertTrue(mController.isHotspotSupported());
}
@Test
public void testHotspotSupported_rightConditions() {
mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+
+ assertTrue(mController.isHotspotSupported());
+
mTetheringCallbackCaptor.getValue()
.onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
@@ -154,13 +156,21 @@
}
@Test
- public void testHotspotSupported_callbackCalledOnChange() {
+ public void testHotspotSupported_callbackCalledOnChange_tetheringSupported() {
mController.addCallback(mCallback1);
- mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+ mTetheringCallbackCaptor.getValue().onTetheringSupported(false);
+
+ verify(mCallback1).onHotspotAvailabilityChanged(false);
+ }
+
+ @Test
+ public void testHotspotSupported_callbackCalledOnChange_tetherableInterfaces() {
+ when(mTetheringInterfaceRegexps.getTetherableWifiRegexs())
+ .thenReturn(Collections.emptyList());
+ mController.addCallback(mCallback1);
mTetheringCallbackCaptor.getValue()
.onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
- verify(mCallback1).onHotspotAvailabilityChanged(true);
+ verify(mCallback1).onHotspotAvailabilityChanged(false);
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 32da4c9..9c250c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -72,7 +72,7 @@
testSsid);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
- null);
+ testSsid);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 0c9130d0..e6b0440 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -86,6 +86,7 @@
mContext.addMockService(comp, mKeyChainService);
when(mUserManager.getUserInfo(anyInt())).thenReturn(new UserInfo());
+ when(mUserManager.isUserUnlocked(any())).thenReturn(true);
when(mKeyChainService.getUserCaAliases())
.thenReturn(new StringParceledListSlice(new ArrayList<String>()));
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 3111ab7..0c37235 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -95,11 +95,7 @@
// TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
sdk_version: "core_platform",
privileged: true,
- // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
- // explicitly.
jni_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
"libtetherutilsjni",
],
resource_dirs: [
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index 789019c..2006fb3 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -32,7 +32,12 @@
int32 system_ui_visibility = 8;
bool is_translucent = 9;
string top_activity_component = 10;
- float scale = 11;
+ // deprecated because original width and height are stored now instead of the scale.
+ float legacy_scale = 11 [deprecated=true];
int64 id = 12;
int32 rotation = 13;
+ // The task width when the snapshot was taken
+ int32 task_width = 14;
+ // The task height when the snapshot was taken
+ int32 task_height = 15;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a8a2791..ad21075 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -641,8 +641,8 @@
final SuspendDialogInfo dialogInfo =
mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
suspendingPackage, providerUserId);
- // TODO(b/148035643): Send the original widget intent or ACTION_MAIN as an
- // IntentSender to SuspendedAppActivity.
+ // onUnsuspend is null because we don't want to start any activity on
+ // unsuspending from a suspended widget.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
providerPackage, suspendingPackage, dialogInfo, null, null,
providerUserId);
@@ -794,6 +794,8 @@
proto.write(WidgetProto.PROVIDER_PACKAGE, widget.provider.id.componentName.getPackageName());
proto.write(WidgetProto.PROVIDER_CLASS, widget.provider.id.componentName.getClassName());
if (widget.options != null) {
+ proto.write(WidgetProto.RESTORE_COMPLETED,
+ widget.options.getBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED));
proto.write(WidgetProto.MIN_WIDTH,
widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 0));
proto.write(WidgetProto.MIN_HEIGHT,
@@ -2509,7 +2511,8 @@
out.endTag(null, "h");
}
- private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException {
+ private static void serializeAppWidget(XmlSerializer out, Widget widget,
+ boolean saveRestoreCompleted) throws IOException {
out.startTag(null, "g");
out.attribute(null, "id", Integer.toHexString(widget.appWidgetId));
out.attribute(null, "rid", Integer.toHexString(widget.restoredId));
@@ -2528,10 +2531,50 @@
out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0));
out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+ if (saveRestoreCompleted) {
+ boolean restoreCompleted = widget.options.getBoolean(
+ AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED);
+ out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted));
+ }
}
out.endTag(null, "g");
}
+ private static Bundle parseWidgetIdOptions(XmlPullParser parser) {
+ Bundle options = new Bundle();
+ String restoreCompleted = parser.getAttributeValue(null, "restore_completed");
+ if (restoreCompleted != null) {
+ options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED,
+ Boolean.valueOf(restoreCompleted));
+ }
+ String minWidthString = parser.getAttributeValue(null, "min_width");
+ if (minWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+ Integer.parseInt(minWidthString, 16));
+ }
+ String minHeightString = parser.getAttributeValue(null, "min_height");
+ if (minHeightString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+ Integer.parseInt(minHeightString, 16));
+ }
+ String maxWidthString = parser.getAttributeValue(null, "max_width");
+ if (maxWidthString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+ Integer.parseInt(maxWidthString, 16));
+ }
+ String maxHeightString = parser.getAttributeValue(null, "max_height");
+ if (maxHeightString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+ Integer.parseInt(maxHeightString, 16));
+ }
+ String categoryString = parser.getAttributeValue(null, "host_category");
+ if (categoryString != null) {
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+ Integer.parseInt(categoryString, 16));
+ }
+ return options;
+ }
+
@Override
public List<String> getWidgetParticipants(int userId) {
return mBackupRestoreController.getWidgetParticipants(userId);
@@ -3064,7 +3107,7 @@
if (widget.host.getUserId() != userId) {
continue;
}
- serializeAppWidget(out, widget);
+ serializeAppWidget(out, widget, true);
}
Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator();
@@ -3203,34 +3246,7 @@
String restoredIdString = parser.getAttributeValue(null, "rid");
widget.restoredId = (restoredIdString == null) ? 0
: Integer.parseInt(restoredIdString, 16);
-
- Bundle options = new Bundle();
- String minWidthString = parser.getAttributeValue(null, "min_width");
- if (minWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- Integer.parseInt(minWidthString, 16));
- }
- String minHeightString = parser.getAttributeValue(null, "min_height");
- if (minHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- Integer.parseInt(minHeightString, 16));
- }
- String maxWidthString = parser.getAttributeValue(null, "max_width");
- if (maxWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- Integer.parseInt(maxWidthString, 16));
- }
- String maxHeightString = parser.getAttributeValue(null, "max_height");
- if (maxHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- Integer.parseInt(maxHeightString, 16));
- }
- String categoryString = parser.getAttributeValue(null, "host_category");
- if (categoryString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- Integer.parseInt(categoryString, 16));
- }
- widget.options = options;
+ widget.options = parseWidgetIdOptions(parser);
final int hostTag = Integer.parseInt(parser.getAttributeValue(
null, "h"), 16);
@@ -4382,7 +4398,7 @@
if (widget.host.isInPackageForUser(backedupPackage, userId)
|| (provider != null
&& provider.isInPackageForUser(backedupPackage, userId))) {
- serializeAppWidget(out, widget);
+ serializeAppWidget(out, widget, false);
}
}
@@ -4815,36 +4831,6 @@
|| widget.provider.getUserId() == userId);
}
- private Bundle parseWidgetIdOptions(XmlPullParser parser) {
- Bundle options = new Bundle();
- String minWidthString = parser.getAttributeValue(null, "min_width");
- if (minWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- Integer.parseInt(minWidthString, 16));
- }
- String minHeightString = parser.getAttributeValue(null, "min_height");
- if (minHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- Integer.parseInt(minHeightString, 16));
- }
- String maxWidthString = parser.getAttributeValue(null, "max_width");
- if (maxWidthString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- Integer.parseInt(maxWidthString, 16));
- }
- String maxHeightString = parser.getAttributeValue(null, "max_height");
- if (maxHeightString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- Integer.parseInt(maxHeightString, 16));
- }
- String categoryString = parser.getAttributeValue(null, "host_category");
- if (categoryString != null) {
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
- Integer.parseInt(categoryString, 16));
- }
- return options;
- }
-
private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
int pending = 0;
final int N = updates.size();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4da60b8..3180ceb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -99,6 +99,7 @@
"android.hardware.vibrator-java",
"app-compat-annotations",
"framework-tethering-stubs",
+ "ike-stubs",
],
required: [
@@ -126,7 +127,6 @@
"android.hidl.manager-V1.2-java",
"dnsresolver_aidl_interface-V2-java",
"netd_event_listener_interface-java",
- "ike-stubs",
],
plugins: [
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7f98c7f..211da0a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
@@ -36,6 +37,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -88,11 +90,13 @@
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
@@ -177,6 +181,10 @@
/** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */
private ArrayList<ServiceRecord> mTmpCollectionResults = null;
+ /** Mapping from uid to their foreground service AppOpCallbacks (if they have one). */
+ @GuardedBy("mAm")
+ private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>();
+
/**
* For keeping ActiveForegroundApps retaining state while the screen is off.
*/
@@ -685,8 +693,8 @@
if (!r.mAllowWhileInUsePermissionInFgs) {
r.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
- service, r, allowBackgroundActivityStarts);
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
+ callingUid, service, r, allowBackgroundActivityStarts);
}
return cmp;
@@ -1455,7 +1463,9 @@
null, true, false, "");
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
- FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
+ r.mAllowWhileInUsePermissionInFgs);
+ registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
}
r.postNotification();
@@ -1504,9 +1514,11 @@
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+ unregisterAppOpCallbackLocked(r);
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
- FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
+ r.mAllowWhileInUsePermissionInFgs);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
@@ -1527,6 +1539,207 @@
}
}
+ /** Registers an AppOpCallback for monitoring special AppOps for this foreground service. */
+ private void registerAppOpCallbackLocked(@NonNull ServiceRecord r) {
+ if (r.app == null) {
+ return;
+ }
+ final int uid = r.appInfo.uid;
+ AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
+ if (callback == null) {
+ callback = new AppOpCallback(r.app, mAm.getAppOpsManager());
+ mFgsAppOpCallbacks.put(uid, callback);
+ }
+ callback.registerLocked();
+ }
+
+ /** Unregisters a foreground service's AppOpCallback. */
+ private void unregisterAppOpCallbackLocked(@NonNull ServiceRecord r) {
+ final int uid = r.appInfo.uid;
+ final AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
+ if (callback != null) {
+ callback.unregisterLocked();
+ if (callback.isObsoleteLocked()) {
+ mFgsAppOpCallbacks.remove(uid);
+ }
+ }
+ }
+
+ /**
+ * For monitoring when {@link #LOGGED_AP_OPS} AppOps occur by an app while it is holding
+ * at least one foreground service and is not also in the TOP state.
+ * Once the uid no longer holds any foreground services, this callback becomes stale
+ * (marked by {@link #isObsoleteLocked()}) and must no longer be used.
+ *
+ * Methods that end in Locked should only be called while the mAm lock is held.
+ */
+ private static final class AppOpCallback {
+ /** AppOps that should be logged if they occur during a foreground service. */
+ private static final int[] LOGGED_AP_OPS = new int[] {
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_CAMERA
+ };
+
+ private final ProcessRecord mProcessRecord;
+
+ /** Count of acceptances per appop (for LOGGED_AP_OPS) during this fgs session. */
+ @GuardedBy("mCounterLock")
+ private final SparseIntArray mAcceptedOps = new SparseIntArray();
+ /** Count of rejections per appop (for LOGGED_AP_OPS) during this fgs session. */
+ @GuardedBy("mCounterLock")
+ private final SparseIntArray mRejectedOps = new SparseIntArray();
+
+ /** Lock for the purposes of mAcceptedOps and mRejectedOps. */
+ private final Object mCounterLock = new Object();
+
+ /**
+ * AppOp Mode (e.g. {@link AppOpsManager#MODE_ALLOWED} per op.
+ * This currently cannot change without the process being killed, so they are constants.
+ */
+ private final SparseIntArray mAppOpModes = new SparseIntArray();
+
+ /**
+ * Number of foreground services currently associated with this AppOpCallback (i.e.
+ * currently held for this uid).
+ */
+ @GuardedBy("mAm")
+ private int mNumFgs = 0;
+
+ /**
+ * Indicates that this Object is stale and must not be used.
+ * Specifically, when mNumFgs decreases down to 0, the callbacks will be unregistered and
+ * this AppOpCallback is unusable.
+ */
+ @GuardedBy("mAm")
+ private boolean mDestroyed = false;
+
+ private final AppOpsManager mAppOpsManager;
+
+ AppOpCallback(@NonNull ProcessRecord r, @NonNull AppOpsManager appOpsManager) {
+ mProcessRecord = r;
+ mAppOpsManager = appOpsManager;
+ for (int op : LOGGED_AP_OPS) {
+ int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, r.uid, r.info.packageName);
+ mAppOpModes.put(op, mode);
+ }
+ }
+
+ private final AppOpsManager.OnOpNotedListener mOpNotedCallback =
+ new AppOpsManager.OnOpNotedListener() {
+ @Override
+ public void onOpNoted(int op, int uid, String pkgName, int result) {
+ if (uid == mProcessRecord.uid && isNotTop()) {
+ incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
+ }
+ }
+ };
+
+ private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
+ new AppOpsManager.OnOpActiveChangedInternalListener() {
+ @Override
+ public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
+ if (uid == mProcessRecord.uid && active && isNotTop()) {
+ incrementOpCount(op, true);
+ }
+ }
+ };
+
+ private boolean isNotTop() {
+ return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
+ }
+
+ private void incrementOpCount(int op, boolean allowed) {
+ synchronized (mCounterLock) {
+ final SparseIntArray counter = allowed ? mAcceptedOps : mRejectedOps;
+ final int index = counter.indexOfKey(op);
+ if (index < 0) {
+ counter.put(op, 1);
+ } else {
+ counter.setValueAt(index, counter.valueAt(index) + 1);
+ }
+ }
+ }
+
+ void registerLocked() {
+ if (isObsoleteLocked()) {
+ Slog.wtf(TAG, "Trying to register on a stale AppOpCallback.");
+ return;
+ }
+ mNumFgs++;
+ if (mNumFgs == 1) {
+ mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
+ mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
+ }
+ }
+
+ void unregisterLocked() {
+ mNumFgs--;
+ if (mNumFgs <= 0) {
+ mDestroyed = true;
+ logFinalValues();
+ mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
+ mAppOpsManager.stopWatchingActive(mOpActiveCallback);
+ }
+ }
+
+ /**
+ * Indicates that all foreground services for this uid are now over and the callback is
+ * stale and must never be used again.
+ */
+ boolean isObsoleteLocked() {
+ return mDestroyed;
+ }
+
+ private void logFinalValues() {
+ synchronized (mCounterLock) {
+ for (int op : LOGGED_AP_OPS) {
+ final int acceptances = mAcceptedOps.get(op);
+ final int rejections = mRejectedOps.get(op);
+ if (acceptances > 0 || rejections > 0) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
+ mProcessRecord.uid, opToEnum(op),
+ modeToEnum(mAppOpModes.get(op)),
+ acceptances, rejections
+ );
+ }
+ }
+ }
+ }
+
+ /** Maps AppOp mode to atoms.proto enum. */
+ private static int modeToEnum(int mode) {
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_ALLOWED;
+ case AppOpsManager.MODE_IGNORED: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_IGNORED;
+ case AppOpsManager.MODE_FOREGROUND: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_FOREGROUND;
+ default: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_UNKNOWN;
+ }
+ }
+ }
+
+ /** Maps AppOp op value to atoms.proto enum. */
+ private static int opToEnum(int op) {
+ switch (op) {
+ case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
+ case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
+ case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
+ case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
+ default: return FrameworkStatsLog
+ .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
+ }
+ }
+
private void cancelForegroundNotificationLocked(ServiceRecord r) {
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
@@ -1865,9 +2078,9 @@
}
if (!s.mAllowWhileInUsePermissionInFgs) {
- final int callingUid = Binder.getCallingUid();
s.mAllowWhileInUsePermissionInFgs =
- shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingUid,
+ shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
+ Binder.getCallingPid(), Binder.getCallingUid(),
service, s, false);
}
@@ -3136,9 +3349,11 @@
mAm.mAppOpsService.finishOperation(
AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+ unregisterAppOpCallbackLocked(r);
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortInstanceName,
- FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
+ r.mAllowWhileInUsePermissionInFgs);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
}
@@ -4625,7 +4840,8 @@
* @return true if allow, false otherwise.
*/
private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
- int callingUid, Intent intent, ServiceRecord r, boolean allowBackgroundActivityStarts) {
+ int callingPid, int callingUid, Intent intent, ServiceRecord r,
+ boolean allowBackgroundActivityStarts) {
// Is the background FGS start restriction turned on?
if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
return true;
@@ -4635,13 +4851,6 @@
return true;
}
- // Is the service in a whitelist?
- final boolean hasAllowBackgroundActivityStartsToken = r.app != null
- ? r.app.mAllowBackgroundActivityStartsTokens.contains(r) : false;
- if (hasAllowBackgroundActivityStartsToken) {
- return true;
- }
-
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
switch (callingAppId) {
@@ -4660,6 +4869,11 @@
return true;
}
+ if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
+ == PERMISSION_GRANTED) {
+ return true;
+ }
+
// Is the calling UID at PROCESS_STATE_TOP or above?
final boolean isCallingUidTopApp = appIsTopLocked(callingUid);
if (isCallingUidTopApp) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 7cb8458..612fd39 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -178,6 +178,8 @@
float maxRefreshRate = Float.POSITIVE_INFINITY;
int lowestConsideredPriority = Vote.MIN_PRIORITY;
while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+ minRefreshRate = 0f;
+ maxRefreshRate = Float.POSITIVE_INFINITY;
int height = Vote.INVALID_SIZE;
int width = Vote.INVALID_SIZE;
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 63054cf..fd8e159 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -40,7 +40,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
@@ -202,7 +201,7 @@
intent,
/* onFinished= */ null,
/* handler= */ null);
- } catch (IntentSender.SendIntentException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Error sending status feedback.", e);
}
});
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c9c6d51..ceb1cd4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -51,6 +51,7 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_NOT_PERCEPTIBLE;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
@@ -3448,16 +3449,10 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(onlyImportant);
for (ConversationChannelWrapper conversation : conversations) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(conversation.getPkg())
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(
- conversation.getNotificationChannel().getConversationId()));
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
- query, UserHandle.of(UserHandle.getUserId(conversation.getUid())));
- if (shortcuts != null && !shortcuts.isEmpty()) {
- conversation.setShortcutInfo(shortcuts.get(0));
- }
+ conversation.setShortcutInfo(getShortcutInfo(
+ conversation.getNotificationChannel().getConversationId(),
+ conversation.getPkg(),
+ UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
}
return new ParceledListSlice<>(conversations);
}
@@ -3477,16 +3472,10 @@
ArrayList<ConversationChannelWrapper> conversations =
mPreferencesHelper.getConversations(pkg, uid);
for (ConversationChannelWrapper conversation : conversations) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(pkg)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(
- conversation.getNotificationChannel().getConversationId()));
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
- query, UserHandle.of(UserHandle.getUserId(uid)));
- if (shortcuts != null && !shortcuts.isEmpty()) {
- conversation.setShortcutInfo(shortcuts.get(0));
- }
+ conversation.setShortcutInfo(getShortcutInfo(
+ conversation.getNotificationChannel().getConversationId(),
+ pkg,
+ UserHandle.of(UserHandle.getUserId(uid))));
}
return new ParceledListSlice<>(conversations);
}
@@ -5646,6 +5635,8 @@
}
}
+ r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user));
+
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
return;
@@ -5959,20 +5950,33 @@
return false;
}
+ private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (shortcutId == null || packageName == null || user == null) {
+ return null;
+ }
+ LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+ if (packageName != null) {
+ query.setPackage(packageName);
+ }
+ if (shortcutId != null) {
+ query.setShortcutIds(Arrays.asList(shortcutId));
+ }
+ query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
+ List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
+ ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
+ ? shortcuts.get(0)
+ : null;
+ return shortcutInfo;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- if (packageName != null) {
- query.setPackage(packageName);
- }
- if (shortcutId != null) {
- query.setShortcutIds(Arrays.asList(shortcutId));
- }
- query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED);
- List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
- ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return shortcutInfo != null;
+ ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
+ return shortcutInfo != null && shortcutInfo.isLongLived();
}
private void logBubbleError(String key, String failureMessage) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f92e1fc..9d243e4 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -35,6 +35,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.media.AudioAttributes;
import android.media.AudioSystem;
@@ -166,6 +167,7 @@
private boolean mAllowBubble;
private Light mLight;
private boolean mIsNotConversationOverride;
+ private ShortcutInfo mShortcutInfo;
/**
* This list contains system generated smart actions from NAS, app-generated smart actions are
* stored in Notification.actions with isContextual() set to true.
@@ -1338,14 +1340,20 @@
return hasCustomRemoteView && !hasDecoratedStyle;
}
- /** Whether this notification is a conversation notification. */
+ public void setShortcutInfo(ShortcutInfo shortcutInfo) {
+ mShortcutInfo = shortcutInfo;
+ }
+
+ /**
+ * Whether this notification is a conversation notification.
+ */
public boolean isConversation() {
Notification notification = getNotification();
if (mChannel.isDemoted()
|| !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
return false;
}
- if (notification.getShortcutId() == null
+ if (mShortcutInfo == null
&& !FeatureFlagUtils.isEnabled(
mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) {
return false;
@@ -1353,7 +1361,6 @@
if (mIsNotConversationOverride) {
return false;
}
- // STOPSHIP b/137397357: Check shortcut to make a further decision
return true;
}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index e86a42c..f497f11 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -57,6 +57,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
@@ -273,9 +274,7 @@
return null;
}
List<ProviderInfo> providerList = null;
-
- // Map from a package name to the corresponding app info.
- ArrayMap<String, ApplicationInfo> appInfos = null;
+ CachedApplicationInfoGenerator appInfoGenerator = null;
synchronized (mLock) {
for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
final ParsedProvider p = mProviders.mProviders.valueAt(i);
@@ -304,26 +303,15 @@
&& (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
continue;
}
-
- // Make sure we have AppInfo for this provider.
- final PackageUserState state = ps.readUserState(userId);
- ApplicationInfo appInfo =
- (appInfos == null) ? null : appInfos.get(pkg.getPackageName());
- if (appInfo == null) {
- appInfo = PackageInfoUtils.generateApplicationInfo(
- pkg, flags, state, userId, ps);
- if (appInfo == null) {
- // In this case, we should avoid calling generateApplicationInfo() for
- // the same package in subsequent iterations, but appInfo shouldn't be null
- // here, so we don't bother.
- continue;
- }
- if (appInfos == null) {
- appInfos = new ArrayMap<>(4);
- }
- appInfos.put(pkg.getPackageName(), appInfo);
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new CachedApplicationInfoGenerator();
}
- // At this point, appInfo != null.
+ final PackageUserState state = ps.readUserState(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
pkg, p, flags, state, appInfo, userId, ps);
@@ -355,14 +343,20 @@
if (pkg == null) {
return null;
}
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags,
- ps.readUserState(userId), userId, ps);
+ final PackageUserState state = ps.readUserState(userId);
+ ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
}
}
void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
int userId) {
synchronized (mLock) {
+ CachedApplicationInfoGenerator appInfoGenerator = null;
for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
final ParsedProvider p = mProvidersByAuthority.valueAt(i);
if (!p.isSyncable()) {
@@ -384,9 +378,18 @@
if (safeMode && !pkg.isSystem()) {
continue;
}
- final ProviderInfo info =
- PackageInfoUtils.generateProviderInfo(pkg, p, 0,
- ps.readUserState(userId), userId, ps);
+ if (appInfoGenerator == null) {
+ appInfoGenerator = new CachedApplicationInfoGenerator();
+ }
+ final PackageUserState state = ps.readUserState(userId);
+ final ApplicationInfo appInfo =
+ appInfoGenerator.generate(pkg, 0, state, userId, ps);
+ if (appInfo == null) {
+ continue;
+ }
+
+ final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+ pkg, p, 0, state, appInfo, userId, ps);
if (info == null) {
continue;
}
@@ -1731,8 +1734,13 @@
if (userState.instantApp && ps.isUpdateAvailable()) {
return null;
}
+ final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, mFlags, userState, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
- userState, userId, ps);
+ userState, appInfo, userId, ps);
if (pi == null) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4eac79c..f714af03 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5459,7 +5459,13 @@
return null;
}
PackageUserState state = ps.readUserState(userId);
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId, ps);
+ final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, ps);
+ if (appInfo == null) {
+ return null;
+ }
+ return PackageInfoUtils.generateProviderInfo(
+ pkg, p, flags, state, appInfo, userId, ps);
}
}
return null;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index f5ce080..4ab1f396 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -48,7 +48,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
+import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.PackageSetting;
@@ -61,6 +61,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
@@ -72,6 +73,7 @@
* @hide
**/
public class PackageInfoUtils {
+ private static final String TAG = PackageParser2.TAG;
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
@@ -312,35 +314,22 @@
/**
* @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
- *
- * @deprecated use {@link #generateProviderInfo(
- * AndroidPackage, ParsedProvider, int, PackageUserState, ApplicationInfo, int, PackageSetting)}
- * instead and pass {@link ApplicationInfo} explicitly to avoid generating duplicate instances
- * of it.
- */
- @Nullable
- @Deprecated
- public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
- @Nullable PackageSetting pkgSetting) {
- return generateProviderInfo(pkg, p, flags, state, null, userId, pkgSetting);
- }
-
- /**
- * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
*/
@Nullable
public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
@PackageManager.ComponentInfoFlags int flags, PackageUserState state,
- @Nullable ApplicationInfo applicationInfo, int userId,
+ @NonNull ApplicationInfo applicationInfo, int userId,
@Nullable PackageSetting pkgSetting) {
if (p == null) return null;
+ if (applicationInfo == null || !pkg.getPackageName().equals(applicationInfo.packageName)) {
+ Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+ + " actual=" + (applicationInfo == null ? "(null AppInfo)"
+ : applicationInfo.packageName));
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
+ }
if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
return null;
}
- if (applicationInfo == null) {
- applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
- }
ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfo(pkg, p, flags, state,
applicationInfo, userId);
if (info == null) {
@@ -486,4 +475,29 @@
| flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
// @formatter:on
}
+
+ /**
+ * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+ */
+ public static class CachedApplicationInfoGenerator {
+ // Map from a package name to the corresponding app info.
+ private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
+
+ /**
+ * {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+ */
+ @Nullable
+ public ApplicationInfo generate(AndroidPackage pkg,
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
+ if (appInfo != null) {
+ return appInfo;
+ }
+ appInfo = PackageInfoUtils.generateApplicationInfo(
+ pkg, flags, state, userId, pkgSetting);
+ mCache.put(pkg.getPackageName(), appInfo);
+ return appInfo;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f99791a..d561b9c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -42,7 +42,7 @@
*/
public class PackageParser2 {
- private static final String TAG = "PackageParser2";
+ static final String TAG = "PackageParser2";
private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b40c2c1..1b5cc6a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3090,7 +3090,7 @@
event.getAction(), fallbackAction.keyCode,
event.getRepeatCount(), fallbackAction.metaState,
event.getDeviceId(), event.getScanCode(),
- flags, event.getSource(), event.getDisplayId(), null /* hmac */, null);
+ flags, event.getSource(), event.getDisplayId(), null);
if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
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 233417d..059861b 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -16,8 +16,13 @@
package com.android.server.power.batterysaver;
import android.annotation.IntDef;
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatterySaverPolicyConfig;
@@ -183,18 +188,15 @@
private String mEventLogKeys;
/**
- * Whether vibration should *really* be disabled -- i.e. {@link Policy#disableVibration}
- * is true *and* {@link #mAccessibilityEnabled} is false.
- */
- @GuardedBy("mLock")
- private boolean mDisableVibrationEffective;
-
- /**
* Whether accessibility is currently enabled or not.
*/
@GuardedBy("mLock")
private boolean mAccessibilityEnabled;
+ /** Whether the phone is projecting in car mode or not. */
+ @GuardedBy("mLock")
+ private boolean mCarModeEnabled;
+
/** The current default adaptive policy. */
@GuardedBy("mLock")
private Policy mDefaultAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY;
@@ -207,6 +209,13 @@
@GuardedBy("mLock")
private Policy mFullPolicy = DEFAULT_FULL_POLICY;
+ /**
+ * The current effective policy. This is based on the current policy level's policy, with any
+ * required adjustments.
+ */
+ @GuardedBy("mLock")
+ private Policy mEffectivePolicy = OFF_POLICY;
+
@IntDef(prefix = {"POLICY_LEVEL_"}, value = {
POLICY_LEVEL_OFF,
POLICY_LEVEL_ADAPTIVE,
@@ -230,6 +239,20 @@
private final ContentResolver mContentResolver;
private final BatterySavingStats mBatterySavingStats;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED:
+ setCarModeEnabled(true);
+ break;
+ case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED:
+ setCarModeEnabled(false);
+ break;
+ }
+ }
+ };
+
@GuardedBy("mLock")
private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
@@ -263,16 +286,25 @@
final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
- acm.addAccessibilityStateChangeListener((enabled) -> {
- synchronized (mLock) {
- mAccessibilityEnabled = enabled;
- }
- refreshSettings();
- });
- final boolean enabled = acm.isEnabled();
+ acm.addAccessibilityStateChangeListener((enabled) -> setAccessibilityEnabled(enabled));
+ final boolean accessibilityEnabled = acm.isEnabled();
synchronized (mLock) {
- mAccessibilityEnabled = enabled;
+ mAccessibilityEnabled = accessibilityEnabled;
}
+
+ final IntentFilter filter = new IntentFilter(
+ UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
+ filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+ // The ENTER/EXIT_CAR_MODE_PRIORITIZED intents are sent to UserHandle.ALL, so no need to
+ // register as all users here.
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ final boolean carModeEnabled =
+ mContext.getSystemService(UiModeManager.class).getCurrentModeType()
+ == Configuration.UI_MODE_TYPE_CAR;
+ synchronized (mLock) {
+ mCarModeEnabled = carModeEnabled;
+ }
+
onChange(true, null);
}
@@ -299,13 +331,34 @@
PowerManager.invalidatePowerSaveModeCaches();
}
+ /**
+ * Notifies listeners of a policy change on the handler thread only if the current policy level
+ * is not {@link POLICY_LEVEL_OFF}.
+ */
+ private void maybeNotifyListenersOfPolicyChange() {
+ final BatterySaverPolicyListener[] listeners;
+ synchronized (mLock) {
+ if (getPolicyLevelLocked() == POLICY_LEVEL_OFF) {
+ // Current policy is OFF, so there's no change to notify listeners of.
+ return;
+ }
+ // Don't call out to listeners with the lock held.
+ listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
+ }
+
+ mHandler.post(() -> {
+ for (BatterySaverPolicyListener listener : listeners) {
+ listener.onBatterySaverPolicyChanged(this);
+ }
+ });
+ }
+
@Override
public void onChange(boolean selfChange, Uri uri) {
refreshSettings();
}
private void refreshSettings() {
- final BatterySaverPolicyListener[] listeners;
synchronized (mLock) {
// Load the non-device-specific setting.
final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);
@@ -334,16 +387,9 @@
// Nothing of note changed.
return;
}
-
- listeners = mListeners.toArray(new BatterySaverPolicyListener[0]);
}
- // Notify the listeners.
- mHandler.post(() -> {
- for (BatterySaverPolicyListener listener : listeners) {
- listener.onBatterySaverPolicyChanged(this);
- }
- });
+ maybeNotifyListenersOfPolicyChange();
}
@GuardedBy("mLock")
@@ -404,31 +450,63 @@
@GuardedBy("mLock")
private void updatePolicyDependenciesLocked() {
- final Policy currPolicy = getCurrentPolicyLocked();
- // Update the effective vibration policy.
- mDisableVibrationEffective = currPolicy.disableVibration
- && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on.
+ final Policy rawPolicy = getCurrentRawPolicyLocked();
+
+ final int locationMode;
+ if (mCarModeEnabled
+ && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE
+ && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) {
+ // If car projection is enabled, ensure that navigation works.
+ locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+ } else {
+ locationMode = rawPolicy.locationMode;
+ }
+ mEffectivePolicy = new Policy(
+ rawPolicy.adjustBrightnessFactor,
+ rawPolicy.advertiseIsEnabled,
+ rawPolicy.deferFullBackup,
+ rawPolicy.deferKeyValueBackup,
+ rawPolicy.disableAnimation,
+ rawPolicy.disableAod,
+ rawPolicy.disableLaunchBoost,
+ rawPolicy.disableOptionalSensors,
+ rawPolicy.disableSoundTrigger,
+ // Don't disable vibration when accessibility is on.
+ rawPolicy.disableVibration && !mAccessibilityEnabled,
+ rawPolicy.enableAdjustBrightness,
+ rawPolicy.enableDataSaver,
+ rawPolicy.enableFirewall,
+ // Don't force night mode when car projection is enabled.
+ rawPolicy.enableNightMode && !mCarModeEnabled,
+ rawPolicy.enableQuickDoze,
+ rawPolicy.filesForInteractive,
+ rawPolicy.filesForNoninteractive,
+ rawPolicy.forceAllAppsStandby,
+ rawPolicy.forceBackgroundCheck,
+ locationMode
+ );
+
final StringBuilder sb = new StringBuilder();
- if (currPolicy.forceAllAppsStandby) sb.append("A");
- if (currPolicy.forceBackgroundCheck) sb.append("B");
+ if (mEffectivePolicy.forceAllAppsStandby) sb.append("A");
+ if (mEffectivePolicy.forceBackgroundCheck) sb.append("B");
- if (mDisableVibrationEffective) sb.append("v");
- if (currPolicy.disableAnimation) sb.append("a");
- if (currPolicy.disableSoundTrigger) sb.append("s");
- if (currPolicy.deferFullBackup) sb.append("F");
- if (currPolicy.deferKeyValueBackup) sb.append("K");
- if (currPolicy.enableFirewall) sb.append("f");
- if (currPolicy.enableDataSaver) sb.append("d");
- if (currPolicy.enableAdjustBrightness) sb.append("b");
+ if (mEffectivePolicy.disableVibration) sb.append("v");
+ if (mEffectivePolicy.disableAnimation) sb.append("a");
+ if (mEffectivePolicy.disableSoundTrigger) sb.append("s");
+ if (mEffectivePolicy.deferFullBackup) sb.append("F");
+ if (mEffectivePolicy.deferKeyValueBackup) sb.append("K");
+ if (mEffectivePolicy.enableFirewall) sb.append("f");
+ if (mEffectivePolicy.enableDataSaver) sb.append("d");
+ if (mEffectivePolicy.enableAdjustBrightness) sb.append("b");
- if (currPolicy.disableLaunchBoost) sb.append("l");
- if (currPolicy.disableOptionalSensors) sb.append("S");
- if (currPolicy.disableAod) sb.append("o");
- if (currPolicy.enableQuickDoze) sb.append("q");
+ if (mEffectivePolicy.disableLaunchBoost) sb.append("l");
+ if (mEffectivePolicy.disableOptionalSensors) sb.append("S");
+ if (mEffectivePolicy.disableAod) sb.append("o");
+ if (mEffectivePolicy.enableQuickDoze) sb.append("q");
- sb.append(currPolicy.locationMode);
+ sb.append(mEffectivePolicy.locationMode);
mEventLogKeys = sb.toString();
}
@@ -857,7 +935,7 @@
return builder.setBatterySaverEnabled(currPolicy.disableSoundTrigger)
.build();
case ServiceType.VIBRATION:
- return builder.setBatterySaverEnabled(mDisableVibrationEffective)
+ return builder.setBatterySaverEnabled(currPolicy.disableVibration)
.build();
case ServiceType.FORCE_ALL_APPS_STANDBY:
return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby)
@@ -933,6 +1011,10 @@
}
private Policy getCurrentPolicyLocked() {
+ return mEffectivePolicy;
+ }
+
+ private Policy getCurrentRawPolicyLocked() {
switch (getPolicyLevelLocked()) {
case POLICY_LEVEL_FULL:
return mFullPolicy;
@@ -994,11 +1076,13 @@
pw.println(" value: " + mAdaptiveDeviceSpecificSettings);
pw.println(" mAccessibilityEnabled=" + mAccessibilityEnabled);
+ pw.println(" mCarModeEnabled=" + mCarModeEnabled);
pw.println(" mPolicyLevel=" + getPolicyLevelLocked());
dumpPolicyLocked(pw, " ", "full", mFullPolicy);
dumpPolicyLocked(pw, " ", "default adaptive", mDefaultAdaptivePolicy);
dumpPolicyLocked(pw, " ", "current adaptive", mAdaptivePolicy);
+ dumpPolicyLocked(pw, " ", "effective", mEffectivePolicy);
}
}
@@ -1009,11 +1093,7 @@
pw.print(indent);
pw.println(" " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled);
pw.print(indent);
- pw.println(" " + KEY_VIBRATION_DISABLED + ":config=" + p.disableVibration);
- // mDisableVibrationEffective is based on the currently selected policy
- pw.print(indent);
- pw.println(" " + KEY_VIBRATION_DISABLED + ":effective=" + (p.disableVibration
- && !mAccessibilityEnabled));
+ pw.println(" " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration);
pw.print(indent);
pw.println(" " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation);
pw.print(indent);
@@ -1070,10 +1150,24 @@
}
@VisibleForTesting
- public void setAccessibilityEnabledForTest(boolean enabled) {
+ void setAccessibilityEnabled(boolean enabled) {
synchronized (mLock) {
- mAccessibilityEnabled = enabled;
- updatePolicyDependenciesLocked();
+ if (mAccessibilityEnabled != enabled) {
+ mAccessibilityEnabled = enabled;
+ updatePolicyDependenciesLocked();
+ maybeNotifyListenersOfPolicyChange();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void setCarModeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (mCarModeEnabled != enabled) {
+ mCarModeEnabled = enabled;
+ updatePolicyDependenciesLocked();
+ maybeNotifyListenersOfPolicyChange();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 68a7188..4fea36c 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -275,8 +275,9 @@
}
/** @return {@code true} if the activity matches a launched activity in this transition. */
- boolean contains(ActivityRecord r) {
- return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
+ boolean contains(WindowContainer wc) {
+ final ActivityRecord r = AppTransitionController.getAppFromContainer(wc);
+ return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
}
/** Called when the activity is drawn or won't be drawn. */
@@ -435,10 +436,10 @@
/** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
@Nullable
- private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
+ private TransitionInfo getActiveTransitionInfo(WindowContainer wc) {
for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
final TransitionInfo info = mTransitionInfoList.get(i);
- if (info.contains(r)) {
+ if (info.contains(wc)) {
return info;
}
}
@@ -623,19 +624,19 @@
* @param activityToReason A map from activity to a reason integer, which must be on of
* ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
+ void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
final long timestampNs = SystemClock.elapsedRealtimeNanos();
for (int index = activityToReason.size() - 1; index >= 0; index--) {
- final ActivityRecord r = activityToReason.keyAt(index);
- final TransitionInfo info = getActiveTransitionInfo(r);
+ final WindowContainer wc = activityToReason.keyAt(index);
+ final TransitionInfo info = getActiveTransitionInfo(wc);
if (info == null || info.mLoggedTransitionStarting) {
// Ignore any subsequent notifyTransitionStarting.
continue;
}
if (DEBUG_METRICS) {
- Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
+ Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info);
}
info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d715ed4..e5b8403 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -40,7 +40,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.activityTypeToString;
@@ -106,7 +105,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_UNSET;
@@ -284,7 +282,6 @@
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.InputApplicationHandle;
-import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -577,12 +574,6 @@
*/
private boolean mCurrentLaunchCanTurnScreenOn = true;
- /**
- * This leash is used to "freeze" the app surface in place after the state change, but before
- * the animation is ready to start.
- */
- private SurfaceControl mTransitChangeLeash = null;
-
/** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
private boolean mLastSurfaceShowing = true;
@@ -1329,15 +1320,6 @@
mDisplayContent.executeAppTransition();
}
- if (prevDc.mChangingApps.remove(this)) {
- // This gets called *after* the ActivityRecord has been reparented to the new display.
- // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
- // so this token is now "frozen" while waiting for the animation to start on prevDc
- // (which will be cancelled since the window is no-longer a child). However, since this
- // is no longer a child of prevDc, this won't be notified of the cancelled animation,
- // so we need to cancel the change transition here.
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
prevDc.mClosingApps.remove(this);
if (prevDc.mFocusedApp == this) {
@@ -3092,7 +3074,7 @@
commitVisibility(false /* visible */, true /* performLayout */);
getDisplayContent().mOpeningApps.remove(this);
- getDisplayContent().mChangingApps.remove(this);
+ getDisplayContent().mChangingContainers.remove(this);
getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
mWmService.mTaskSnapshotController.onAppRemoved(this);
mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
@@ -3995,13 +3977,11 @@
appToken, visible, appTransition, isVisible(), mVisibleRequested,
Debug.getCallers(6));
+ onChildVisibilityRequested(visible);
+
final DisplayContent displayContent = getDisplayContent();
displayContent.mOpeningApps.remove(this);
displayContent.mClosingApps.remove(this);
- if (isInChangeTransition()) {
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
- }
- displayContent.mChangingApps.remove(this);
waitingToShow = false;
mVisibleRequested = visible;
mLastDeferHidingClient = deferHidingClient;
@@ -5805,11 +5785,6 @@
return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
}
- @Override
- boolean isChangingAppTransition() {
- return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
- }
-
/**
* Creates a layer to apply crop to an animation.
*/
@@ -5830,84 +5805,19 @@
this, endDeferFinishCallback);
}
- private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getDisplayContent().mAppTransition.isTransitionSet()
- || getSurfaceControl() == null) {
- return false;
- }
- // Only do an animation into and out-of freeform mode for now. Other mode
- // transition animations are currently handled by system-ui.
- return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
- }
-
@Override
boolean isWaitingForTransitionStart() {
final DisplayContent dc = getDisplayContent();
return dc != null && dc.mAppTransition.isTransitionSet()
&& (dc.mOpeningApps.contains(this)
|| dc.mClosingApps.contains(this)
- || dc.mChangingApps.contains(this));
+ || dc.mChangingContainers.contains(this));
}
- /**
- * Initializes a change transition. Because the app is visible already, there is a small period
- * of time where the user can see the app content/window update before the transition starts.
- * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
- * "freezes" the location/crop until the transition starts.
- * <p>
- * Here's a walk-through of the process:
- * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
- * 2. Set the temporary leash's position/crop to the current state.
- * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
- * 4. Once the transition is ready, it will reparent the app to the animation leash.
- * 5. Detach the interim-change-leash.
- */
- private void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
- false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
- mDisplayContent.mChangingApps.add(this);
- mTransitStartRect.set(startBounds);
-
- final SurfaceControl.Builder builder = makeAnimationLeash()
- .setParent(getAnimationLeashParent())
- .setName(getSurfaceControl() + " - interim-change-leash");
- mTransitChangeLeash = builder.build();
- Transaction t = getPendingTransaction();
- t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
- t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
- t.show(mTransitChangeLeash);
- t.reparent(getSurfaceControl(), mTransitChangeLeash);
- onAnimationLeashCreated(t, mTransitChangeLeash);
-
- // Skip creating snapshot if this transition is controlled by a remote animator which
- // doesn't need it.
- ArraySet<Integer> activityTypes = new ArraySet<>();
- activityTypes.add(getActivityType());
- RemoteAnimationAdapter adapter =
- mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
- this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
- if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
- return;
- }
-
- if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
- SurfaceControl.ScreenshotGraphicBuffer snapshot =
- mWmService.mTaskSnapshotController.createTaskSnapshot(
- task, 1 /* scaleFraction */);
- if (snapshot != null) {
- mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, t, this,
- snapshot.getGraphicBuffer(), true /* relative */);
- }
- }
- }
-
- @Override
- public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ private int getAnimationLayer() {
// The leash is parented to the animation layer. We need to preserve the z-order by using
// the prefix order index, but we boost if necessary.
- int layer = 0;
+ int layer;
if (!inPinnedWindowingMode()) {
layer = getPrefixOrderIndex();
} else {
@@ -5920,21 +5830,17 @@
if (mNeedsZBoost) {
layer += Z_BOOST_BASE;
}
- if (!mNeedsAnimationBoundsLayer) {
- t.setLayer(leash, layer);
- }
+ return layer;
+ }
- final DisplayContent dc = getDisplayContent();
- dc.assignStackOrdering();
+ @Override
+ public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
+ t.setLayer(leash, getAnimationLayer());
+ getDisplayContent().assignStackOrdering();
+ }
- if (leash == mTransitChangeLeash) {
- // This is a temporary state so skip any animation notifications
- return;
- } else if (mTransitChangeLeash != null) {
- // unparent mTransitChangeLeash for clean-up
- clearChangeLeash(t, false /* cancel */);
- }
-
+ @Override
+ public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
if (mAnimatingActivityRegistry != null) {
mAnimatingActivityRegistry.notifyStarting(this);
}
@@ -5962,7 +5868,8 @@
// surface size has already same as the animating container.
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
}
- t.setLayer(mAnimationBoundsLayer, layer);
+ t.setLayer(leash, 0);
+ t.setLayer(mAnimationBoundsLayer, getAnimationLayer());
// Reparent leash to animation bounds layer.
t.reparent(leash, mAnimationBoundsLayer);
@@ -5994,10 +5901,6 @@
return mLastSurfaceShowing;
}
- boolean isInChangeTransition() {
- return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
- }
-
void attachThumbnailAnimation() {
if (!isAnimating(PARENTS)) {
return;
@@ -6134,31 +6037,6 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- /**
- * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
- * to another leash.
- */
- private void clearChangeLeash(Transaction t, boolean cancel) {
- if (mTransitChangeLeash == null) {
- return;
- }
- if (cancel) {
- clearThumbnail();
- SurfaceControl sc = getSurfaceControl();
- SurfaceControl parentSc = getParentSurfaceControl();
- // Don't reparent if surface is getting destroyed
- if (parentSc != null && sc != null) {
- t.reparent(sc, getParentSurfaceControl());
- }
- }
- t.hide(mTransitChangeLeash);
- t.remove(mTransitChangeLeash);
- mTransitChangeLeash = null;
- if (cancel) {
- onAnimationLeashLost(t);
- }
- }
-
void clearAnimatingFlags() {
boolean wallpaperMightChange = false;
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -6174,7 +6052,7 @@
void cancelAnimation() {
cancelAnimationOnly();
clearThumbnail();
- clearChangeLeash(getPendingTransaction(), true /* cancel */);
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
}
/**
@@ -6219,6 +6097,7 @@
mRemoteAnimationDefinition = null;
}
+ @Override
RemoteAnimationDefinition getRemoteAnimationDefinition() {
return mRemoteAnimationDefinition;
}
@@ -6679,8 +6558,6 @@
return;
}
}
- final int prevWinMode = getWindowingMode();
- mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
if (shouldUseSizeCompatMode()) {
@@ -6705,12 +6582,6 @@
}
}
- final int newWinMode = getWindowingMode();
- if ((prevWinMode != newWinMode) && (mDisplayContent != null)
- && shouldStartChangeTransition(prevWinMode, newWinMode)) {
- initializeChangeTransition(mTmpPrevBounds);
- }
-
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
// configuration, but it's used to push configuration changes so explicitly update that.
@@ -7595,6 +7466,7 @@
}
}
+ @Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 693a5e4..d2d7ad3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5868,8 +5868,8 @@
* Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home
* activities.
*
- * @param preferredPackage Specify a preferred package name, otherwise use secondary home
- * component defined in config_secondaryHomeComponent.
+ * @param preferredPackage Specify a preferred package name, otherwise use the package name
+ * defined in config_secondaryHomePackage.
* @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME}
*/
Intent getSecondaryHomeIntent(String preferredPackage) {
@@ -5877,10 +5877,10 @@
final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
if (preferredPackage == null || useSystemProvidedLauncher) {
- // Using the component stored in config if no package name or forced.
- final String secondaryHomeComponent = mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
+ // Using the package name stored in config if no preferred package name or forced.
+ final String secondaryHomePackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ intent.setPackage(secondaryHomePackage);
} else {
intent.setPackage(preferredPackage);
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 8cf0881..ca09537 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -2308,14 +2308,14 @@
}
notifyAppTransitionTimeoutLocked();
if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
- || !dc.mChangingApps.isEmpty()) {
+ || !dc.mChangingContainers.isEmpty()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
+ "mOpeningApps.size()=%d mClosingApps.size()=%d "
+ "mChangingApps.size()=%d",
dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
dc.mOpeningApps.size(), dc.mClosingApps.size(),
- dc.mChangingApps.size());
+ dc.mChangingContainers.size());
setTimeout();
mService.mWindowPlacerLocked.performSurfacePlacement();
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3f4e791..0912b2e 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -55,6 +55,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -86,7 +87,7 @@
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
- private final ArrayMap<ActivityRecord, Integer> mTempTransitionReasons = new ArrayMap<>();
+ private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -104,7 +105,8 @@
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
- || !transitionGoodToGo(mDisplayContent.mChangingApps, mTempTransitionReasons)) {
+ || !transitionGoodToGo(mDisplayContent.mChangingContainers,
+ mTempTransitionReasons)) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -130,17 +132,21 @@
// transition selection depends on wallpaper target visibility.
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
- appCount = mDisplayContent.mChangingApps.size();
+ appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
- mDisplayContent.mChangingApps.valueAtUnchecked(i).clearAnimatingFlags();
+ final ActivityRecord activity = getAppFromContainer(
+ mDisplayContent.mChangingContainers.valueAtUnchecked(i));
+ if (activity != null) {
+ activity.clearAnimatingFlags();
+ }
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
- mDisplayContent.mOpeningApps, mDisplayContent.mChangingApps);
+ mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
@@ -159,7 +165,7 @@
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
- mDisplayContent.mClosingApps, mDisplayContent.mChangingApps);
+ mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final boolean allowAnimations = mDisplayContent.getDisplayPolicy().allowAppAnimationsLw();
final ActivityRecord animLpActivity = allowAnimations
? findAnimLayoutParamsToken(transit, activityTypes)
@@ -171,14 +177,13 @@
? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
: null;
final ActivityRecord topChangingApp = allowAnimations
- ? getTopApp(mDisplayContent.mChangingApps, false /* ignoreHidden */)
+ ? getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */)
: null;
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mOpeningApps)
- || containsVoiceInteraction(mDisplayContent.mChangingApps);
+ || containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
@@ -206,7 +211,7 @@
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
- mDisplayContent.mChangingApps.clear();
+ mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
@@ -235,9 +240,9 @@
return mainWindow != null ? mainWindow.mAttrs : null;
}
- RemoteAnimationAdapter getRemoteAnimationOverride(ActivityRecord animLpActivity,
+ RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container,
@TransitionType int transit, ArraySet<Integer> activityTypes) {
- final RemoteAnimationDefinition definition = animLpActivity.getRemoteAnimationDefinition();
+ final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
if (definition != null) {
final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
if (adapter != null) {
@@ -271,6 +276,11 @@
}
}
+ static ActivityRecord getAppFromContainer(WindowContainer wc) {
+ return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
+ : wc.asActivityRecord();
+ }
+
/**
* @return The window token that determines the animation theme.
*/
@@ -279,14 +289,14 @@
ActivityRecord result;
final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
- final ArraySet<ActivityRecord> changingApps = mDisplayContent.mChangingApps;
+ final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers;
// Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
- return result;
+ return getAppFromContainer(result);
}
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);
@@ -302,7 +312,7 @@
* of apps in {@code array1}, {@code array2}, and {@code array3}.
*/
private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
- ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3) {
+ ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) {
final ArraySet<Integer> result = new ArraySet<>();
for (int i = array1.size() - 1; i >= 0; i--) {
result.add(array1.valueAt(i).getActivityType());
@@ -317,7 +327,7 @@
}
private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1,
- ArraySet<ActivityRecord> array2, ArraySet<ActivityRecord> array3,
+ ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3,
Predicate<ActivityRecord> filter) {
final int array2base = array1.size();
final int array3base = array2.size() + array2base;
@@ -325,15 +335,16 @@
int bestPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord bestToken = null;
for (int i = 0; i < count; i++) {
- final ActivityRecord wtoken = i < array2base
+ final WindowContainer wtoken = i < array2base
? array1.valueAt(i)
: (i < array3base
? array2.valueAt(i - array2base)
: array3.valueAt(i - array3base));
final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
- if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
+ final ActivityRecord r = getAppFromContainer(wtoken);
+ if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) {
bestPrefixOrderIndex = prefixOrderIndex;
- bestToken = wtoken;
+ bestToken = r;
}
}
return bestToken;
@@ -589,21 +600,13 @@
}
private void handleChangingApps(@TransitionType int transit) {
- final ArraySet<ActivityRecord> apps = mDisplayContent.mChangingApps;
+ final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();
for (int i = 0; i < appsCount; i++) {
- ActivityRecord activity = apps.valueAt(i);
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", activity);
- activity.cancelAnimationOnly();
- activity.applyAnimation(null, transit, true, false,
+ WindowContainer wc = apps.valueAt(i);
+ ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
+ wc.applyAnimation(null, transit, true, false,
null /* animationFinishedCallback */);
- activity.updateReportedVisibilityLocked();
- mService.openSurfaceTransaction();
- try {
- activity.showAllWindowsLocked();
- } finally {
- mService.closeSurfaceTransaction("handleChangingApps");
- }
}
}
@@ -628,8 +631,8 @@
}
}
- private boolean transitionGoodToGo(ArraySet<ActivityRecord> apps,
- ArrayMap<ActivityRecord, Integer> outReasons) {
+ private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
+ ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
@@ -652,13 +655,17 @@
return false;
}
for (int i = 0; i < apps.size(); i++) {
- ActivityRecord activity = apps.valueAt(i);
+ WindowContainer wc = apps.valueAt(i);
+ final ActivityRecord activity = getAppFromContainer(wc);
+ if (activity == null) {
+ continue;
+ }
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
- + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
- activity, activity.allDrawn, activity.startingDisplayed,
- activity.startingMoved, activity.isRelaunching(),
- activity.startingWindow);
+ "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
+ activity, activity.allDrawn, activity.startingDisplayed,
+ activity.startingMoved, activity.isRelaunching(),
+ activity.startingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
@@ -838,7 +845,7 @@
@VisibleForTesting
boolean isTransitWithinTask(@TransitionType int transit, Task task) {
if (task == null
- || !mDisplayContent.mChangingApps.isEmpty()) {
+ || !mDisplayContent.mChangingContainers.isEmpty()) {
// if there is no task, then we can't constrain to the task.
// if anything is changing, it can animate outside its task.
return false;
@@ -882,12 +889,13 @@
* {@link ActivityRecord#isVisible}.
* @return The top {@link ActivityRecord}.
*/
- private ActivityRecord getTopApp(ArraySet<ActivityRecord> apps, boolean ignoreInvisible) {
+ private ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps,
+ boolean ignoreInvisible) {
int topPrefixOrderIndex = Integer.MIN_VALUE;
ActivityRecord topApp = null;
for (int i = apps.size() - 1; i >= 0; i--) {
- final ActivityRecord app = apps.valueAt(i);
- if (ignoreInvisible && !app.isVisible()) {
+ final ActivityRecord app = getAppFromContainer(apps.valueAt(i));
+ if (app == null || ignoreInvisible && !app.isVisible()) {
continue;
}
final int prefixOrderIndex = app.getPrefixOrderIndex();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8ccb59f..30e8da2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -91,7 +91,6 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
@@ -314,7 +313,7 @@
final ArraySet<ActivityRecord> mOpeningApps = new ArraySet<>();
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
- final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
+ final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
private MetricsLogger mMetricsLogger;
@@ -2695,7 +2694,7 @@
// Clear all transitions & screen frozen states when removing display.
mOpeningApps.clear();
mClosingApps.clear();
- mChangingApps.clear();
+ mChangingContainers.clear();
mUnknownAppVisibilityController.clear();
mAppTransition.removeAppTransitionTimeoutCallbacks();
handleAnimatingStoppedAndTransition();
@@ -2712,6 +2711,9 @@
mRemovingDisplay = false;
}
+ // Apply the pending transaction here since we may not be able to reach the DisplayContent
+ // on the next traversal if it's removed from RootWindowContainer child list.
+ getPendingTransaction().apply();
mWmService.mWindowPlacerLocked.requestTraversal();
}
@@ -2913,9 +2915,6 @@
for (int i = mClosingApps.size() - 1; i >= 0; i--) {
mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS);
}
- for (int i = mChangingApps.size() - 1; i >= 0; i--) {
- mChangingApps.valueAt(i).writeIdentifierToProto(proto, CHANGING_APPS);
- }
proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final ActivityStack focusedStack = getFocusedStack();
@@ -3667,7 +3666,7 @@
}
}
- if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingApps.isEmpty()) {
+ if (!mOpeningApps.isEmpty() || !mClosingApps.isEmpty() || !mChangingContainers.isEmpty()) {
pw.println();
if (mOpeningApps.size() > 0) {
pw.print(" mOpeningApps="); pw.println(mOpeningApps);
@@ -3675,8 +3674,8 @@
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
- if (mChangingApps.size() > 0) {
- pw.print(" mChangingApps="); pw.println(mChangingApps);
+ if (mChangingContainers.size() > 0) {
+ pw.print(" mChangingApps="); pw.println(mChangingContainers);
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e69551a..9468bff 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -500,7 +500,7 @@
}
if (mTargetActivityRecord != null) {
- final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>(1);
+ final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1);
reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
mService.mAtmService.mStackSupervisor.getActivityMetricsLogger()
.notifyTransitionStarting(reasons);
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index d2dbab8..0eb9daf 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -387,7 +387,7 @@
final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
if (dc.mOpeningApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_OPENING;
- } else if (dc.mChangingApps.contains(topActivity)) {
+ } else if (dc.mChangingContainers.contains(topActivity)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 7164cd8..1e54e69 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -128,7 +128,8 @@
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
- @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback,
+ @Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
@@ -139,9 +140,14 @@
cancelAnimation();
return;
}
- mLeash = createAnimationLeash(surface, t,
- mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
- mAnimatable.onAnimationLeashCreated(t, mLeash);
+ mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
+ if (mLeash == null) {
+ mLeash = createAnimationLeash(mAnimatable, surface, t,
+ mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
+ 0 /* y */, hidden);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+ }
+ mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
@@ -150,6 +156,12 @@
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
+ @AnimationType int type,
+ @Nullable OnAnimationFinishedCallback animationFinishedCallback) {
+ startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* freezer */);
+ }
+
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
@@ -311,15 +323,31 @@
}
private void reset(Transaction t, boolean destroyLeash) {
- final SurfaceControl surface = mAnimatable.getSurfaceControl();
- final SurfaceControl parent = mAnimatable.getParentSurfaceControl();
+ mService.mAnimationTransferMap.remove(mAnimation);
+ mAnimation = null;
+ mAnimationFinishedCallback = null;
+ mAnimationType = ANIMATION_TYPE_NONE;
+ if (mLeash == null) {
+ return;
+ }
+ SurfaceControl leash = mLeash;
+ mLeash = null;
+ final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
+ if (scheduleAnim) {
+ mService.scheduleAnimationLocked();
+ }
+ }
+ static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
+ boolean destroy) {
boolean scheduleAnim = false;
+ final SurfaceControl surface = animatable.getSurfaceControl();
+ final SurfaceControl parent = animatable.getParentSurfaceControl();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
- final boolean reparent = mLeash != null && surface != null;
+ final boolean reparent = surface != null;
if (reparent) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
// We shouldn't really need these isValid checks but we do
@@ -329,40 +357,30 @@
scheduleAnim = true;
}
}
- mService.mAnimationTransferMap.remove(mAnimation);
- if (mLeash != null && destroyLeash) {
- t.remove(mLeash);
+ if (destroy) {
+ t.remove(leash);
scheduleAnim = true;
}
- mLeash = null;
- mAnimation = null;
- mAnimationFinishedCallback = null;
- mAnimationType = ANIMATION_TYPE_NONE;
if (reparent) {
// Make sure to inform the animatable after the surface was reparented (or reparent
// wasn't possible, but we still need to invoke the callback)
- mAnimatable.onAnimationLeashLost(t);
+ animatable.onAnimationLeashLost(t);
scheduleAnim = true;
}
-
- if (scheduleAnim) {
- mService.scheduleAnimationLocked();
- }
+ return scheduleAnim;
}
- private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
- int height, boolean hidden) {
+ static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
+ Transaction t, int width, int height, int x, int y, boolean hidden) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
- final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
- .setParent(mAnimatable.getAnimationLeashParent())
+ final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
+ .setParent(animatable.getAnimationLeashParent())
.setHidden(hidden)
.setName(surface + " - animation-leash");
final SurfaceControl leash = builder.build();
t.setWindowCrop(leash, width, height);
-
- // TODO: rely on builder.setHidden(hidden) instead of show and setAlpha when b/138459974 is
- // fixed.
+ t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
@@ -489,7 +507,8 @@
void commitPendingTransaction();
/**
- * Called when the was created.
+ * Called when the animation leash is created. Note that this is also called by
+ * {@link SurfaceFreezer}, so this doesn't mean we're about to start animating.
*
* @param t The transaction to use to apply any necessary changes.
* @param leash The leash that was created.
@@ -497,6 +516,14 @@
void onAnimationLeashCreated(Transaction t, SurfaceControl leash);
/**
+ * Called when the animator is about to start animating the leash.
+ *
+ * @param t The transaction to use to apply any necessary changes.
+ * @param leash The leash that was created.
+ */
+ default void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { }
+
+ /**
* Called when the leash is being destroyed, or when the leash is being transferred to
* another SurfaceAnimator.
*
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
new file mode 100644
index 0000000..20435ea
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 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.wm;
+
+import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.function.Supplier;
+
+/**
+ * This class handles "freezing" of an Animatable. The Animatable in question should implement
+ * Freezable.
+ *
+ * The point of this is to enable WindowContainers to each be capable of freezing themselves.
+ * Freezing means taking a snapshot and placing it above everything in the sub-hierarchy.
+ * The "placing above" requires that a parent surface be inserted above the target surface so that
+ * the target surface and the snapshot are siblings.
+ *
+ * The overall flow for a transition using this would be:
+ * 1. Set transition and record animatable in mChangingApps
+ * 2. Call {@link #freeze} to set-up the leashes and cover with a snapshot.
+ * 3. When transition participants are ready, start SurfaceAnimator with this as a parameter
+ * 4. SurfaceAnimator will then {@link #takeLeashForAnimation} instead of creating another leash.
+ * 5. The animation system should eventually clean this up via {@link #unfreeze}.
+ */
+class SurfaceFreezer {
+
+ private final Freezable mAnimatable;
+ private final WindowManagerService mWmService;
+ private SurfaceControl mLeash;
+ Snapshot mSnapshot = null;
+ final Rect mFreezeBounds = new Rect();
+
+ /**
+ * @param animatable The object to animate.
+ */
+ SurfaceFreezer(Freezable animatable, WindowManagerService service) {
+ mAnimatable = animatable;
+ mWmService = service;
+ }
+
+ /**
+ * Freeze the target surface. This is done by creating a leash (inserting a parent surface
+ * above the target surface) and then taking a snapshot and placing it over the target surface.
+ *
+ * @param startBounds The original bounds (on screen) of the surface we are snapshotting.
+ */
+ void freeze(SurfaceControl.Transaction t, Rect startBounds) {
+ mFreezeBounds.set(startBounds);
+
+ mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
+ t, startBounds.width(), startBounds.height(), startBounds.left, startBounds.top,
+ false /* hidden */);
+ mAnimatable.onAnimationLeashCreated(t, mLeash);
+
+ SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
+ if (freezeTarget != null) {
+ GraphicBuffer snapshot = createSnapshotBuffer(freezeTarget, startBounds);
+ if (snapshot != null) {
+ mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, snapshot, mLeash);
+ }
+ }
+ }
+
+ /**
+ * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
+ * By transferring the leash, this will no longer try to clean-up the leash when finished.
+ */
+ SurfaceControl takeLeashForAnimation() {
+ SurfaceControl out = mLeash;
+ mLeash = null;
+ return out;
+ }
+
+ /**
+ * Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the
+ * snapshot.
+ */
+ void unfreeze(SurfaceControl.Transaction t) {
+ if (mSnapshot != null) {
+ mSnapshot.destroy(t);
+ }
+ if (mLeash == null) {
+ return;
+ }
+ SurfaceControl leash = mLeash;
+ mLeash = null;
+ final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash,
+ false /* destroy */);
+ if (scheduleAnim) {
+ mWmService.scheduleAnimationLocked();
+ }
+ }
+
+ boolean hasLeash() {
+ return mLeash != null;
+ }
+
+ private static GraphicBuffer createSnapshotBuffer(@NonNull SurfaceControl target,
+ @Nullable Rect bounds) {
+ Rect cropBounds = null;
+ if (bounds != null) {
+ cropBounds = new Rect(bounds);
+ cropBounds.offsetTo(0, 0);
+ }
+ final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(
+ target, cropBounds, 1.f /* frameScale */, PixelFormat.RGBA_8888);
+ final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
+ : null;
+ if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
+ return null;
+ }
+ return buffer;
+ }
+
+ class Snapshot {
+ private SurfaceControl mSurfaceControl;
+ private AnimationAdapter mAnimation;
+ private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback;
+
+ /**
+ * @param t Transaction to create the thumbnail in.
+ * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with.
+ */
+ Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
+ GraphicBuffer thumbnailHeader, SurfaceControl parent) {
+ Surface drawSurface = surfaceFactory.get();
+ // We can't use a delegating constructor since we need to
+ // reference this::onAnimationFinished
+ final int width = thumbnailHeader.getWidth();
+ final int height = thumbnailHeader.getHeight();
+
+ mSurfaceControl = mAnimatable.makeAnimationLeash()
+ .setName("snapshot anim: " + mAnimatable.toString())
+ .setBufferSize(width, height)
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .setParent(parent)
+ .build();
+
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl);
+
+ // Transfer the thumbnail to the surface
+ drawSurface.copyFrom(mSurfaceControl);
+ drawSurface.attachAndQueueBuffer(thumbnailHeader);
+ drawSurface.release();
+ t.show(mSurfaceControl);
+
+ // We parent the thumbnail to the container, and just place it on top of anything else
+ // in the container.
+ t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
+ }
+
+ void destroy(SurfaceControl.Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ t.remove(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+
+ /**
+ * Starts an animation.
+ *
+ * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
+ * component responsible for running the animation. It runs the animation with
+ * {@link AnimationAdapter#startAnimation} once the hierarchy with
+ * the Leash has been set up.
+ * @param animationFinishedCallback The callback being triggered when the animation
+ * finishes.
+ */
+ void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type,
+ @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) {
+ cancelAnimation(t, true /* restarting */);
+ mAnimation = anim;
+ mFinishedCallback = animationFinishedCallback;
+ if (mSurfaceControl == null) {
+ cancelAnimation(t, false /* restarting */);
+ return;
+ }
+ mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback);
+ }
+
+ /**
+ * Cancels the animation, and resets the leash.
+ *
+ * @param t The transaction to use for all cancelling surface operations.
+ * @param restarting Whether we are restarting the animation.
+ */
+ private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
+ final SurfaceControl leash = mSurfaceControl;
+ final AnimationAdapter animation = mAnimation;
+ final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
+ mFinishedCallback;
+ mAnimation = null;
+ mFinishedCallback = null;
+ if (animation != null) {
+ animation.onAnimationCancelled(leash);
+ if (!restarting) {
+ if (animationFinishedCallback != null) {
+ animationFinishedCallback.onAnimationFinished(
+ ANIMATION_TYPE_APP_TRANSITION, animation);
+ }
+ }
+ }
+ if (!restarting) {
+ // TODO: do we need to destroy?
+ destroy(t);
+ }
+ }
+ }
+
+ /** freezable */
+ public interface Freezable extends SurfaceAnimator.Animatable {
+ /**
+ * @return The surface to take a snapshot of. If this returns {@code null}, no snapshot
+ * will be generated (but the rest of the freezing logic will still happen).
+ */
+ @Nullable SurfaceControl getFreezeSnapshotTarget();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 76805e9..07e17e8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -58,6 +58,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
+import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
@@ -78,6 +79,9 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -119,10 +123,13 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.ITaskOrganizer;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -1884,6 +1891,8 @@
.setBounds(mLastNonFullscreenBounds);
}
+ final int prevWinMode = getWindowingMode();
+ mTmpPrevBounds.set(getBounds());
final boolean wasInMultiWindowMode = inMultiWindowMode();
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
@@ -1891,6 +1900,12 @@
updateShadowsRadius(isFocused(), getPendingTransaction());
}
+ final int newWinMode = getWindowingMode();
+ if ((prevWinMode != newWinMode) && (mDisplayContent != null)
+ && shouldStartChangeTransition(prevWinMode, newWinMode)) {
+ initializeChangeTransition(mTmpPrevBounds);
+ }
+
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
// current (non-fullscreen) bounds for persistence.
if (getWindowConfiguration().persistTaskBounds()) {
@@ -1905,6 +1920,63 @@
}
/**
+ * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+ */
+ private void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
+ false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
+ mDisplayContent.mChangingContainers.add(this);
+
+ mSurfaceFreezer.freeze(getPendingTransaction(), startBounds);
+ }
+
+ private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
+ if (mWmService.mDisableTransitionAnimation
+ || !isVisible()
+ || getDisplayContent().mAppTransition.isTransitionSet()
+ || getSurfaceControl() == null) {
+ return false;
+ }
+ // Only do an animation into and out-of freeform mode for now. Other mode
+ // transition animations are currently handled by system-ui.
+ return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
+ }
+
+ @VisibleForTesting
+ boolean isInChangeTransition() {
+ return mSurfaceFreezer.hasLeash() || AppTransition.isChangeTransit(mTransit);
+ }
+
+ @Override
+ public SurfaceControl getFreezeSnapshotTarget() {
+ final int transit = mDisplayContent.mAppTransition.getAppTransition();
+ if (!AppTransition.isChangeTransit(transit)) {
+ return null;
+ }
+ // Skip creating snapshot if this transition is controlled by a remote animator which
+ // doesn't need it.
+ final ArraySet<Integer> activityTypes = new ArraySet<>();
+ activityTypes.add(getActivityType());
+ final RemoteAnimationAdapter adapter =
+ mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
+ this, transit, activityTypes);
+ if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
+ return null;
+ }
+ return getSurfaceControl();
+ }
+
+ @Override
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, mUserId);
+ proto.write(TITLE, intent != null && intent.getComponent() != null
+ ? intent.getComponent().flattenToShortString() : "Task");
+ proto.end(token);
+ }
+
+ /**
* Saves launching state if necessary so that we can launch the activity to its latest state.
* It only saves state if this task has been shown to user and it's in fullscreen or freeform
* mode on freeform displays.
@@ -2614,12 +2686,22 @@
if (!isRootTask) {
adjustBoundsForDisplayChangeIfNeeded(dc);
}
+ final DisplayContent prevDc = mDisplayContent;
super.onDisplayChanged(dc);
if (!isRootTask) {
final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
mTaskId, displayId);
}
+ if (prevDc != null && prevDc.mChangingContainers.remove(this)) {
+ // This gets called *after* this has been reparented to the new display.
+ // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
+ // so this token is now "frozen" while waiting for the animation to start on prevDc
+ // (which will be cancelled since the window is no-longer a child). However, since this
+ // is no longer a child of prevDc, this won't be notified of the cancelled animation,
+ // so we need to cancel the change transition here.
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
+ }
}
/**
@@ -3010,15 +3092,6 @@
return forAllTasks((t) -> { return t != this && t.isTaskAnimating(); });
}
- /**
- * @return {@code true} if changing app transition is running.
- */
- @Override
- boolean isChangingAppTransition() {
- final ActivityRecord activity = getTopVisibleActivity();
- return activity != null && getDisplayContent().mChangingApps.contains(activity);
- }
-
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 5c73f92..f83b052 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -18,19 +18,18 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static com.android.server.wm.TaskSnapshotPersister.DISABLE_HIGH_RES_BITMAPS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -89,14 +88,6 @@
@VisibleForTesting
static final int SNAPSHOT_MODE_NONE = 2;
- /**
- * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is
- * interpreted as using the most appropriate scale ratio for the system.
- * This may yield a smaller ratio on low memory devices.
- */
- @VisibleForTesting
- static final float SNAPSHOT_SCALE_AUTO = -1f;
-
private final WindowManagerService mService;
private final TaskSnapshotCache mCache;
@@ -229,7 +220,7 @@
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
boolean isLowResolution) {
return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution
- || DISABLE_HIGH_RES_BITMAPS);
+ && mPersister.enableLowResSnapshots());
}
/**
@@ -273,8 +264,6 @@
* information from the task and populates the builder.
*
* @param task the task to capture
- * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO}
- * to automatically select
* @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
* automatically select
* @param builder the snapshot builder to populate
@@ -282,8 +271,7 @@
* @return true if the state of the task is ok to proceed
*/
@VisibleForTesting
- boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat,
- TaskSnapshot.Builder builder) {
+ boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) {
if (!mService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -314,18 +302,6 @@
builder.setId(System.currentTimeMillis());
builder.setContentInsets(getInsets(mainWindow));
- final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
-
- if (scaleFraction == SNAPSHOT_SCALE_AUTO) {
- builder.setScaleFraction(isLowRamDevice
- ? mPersister.getLowResScale()
- : mHighResTaskSnapshotScale);
- builder.setIsLowResolution(isLowRamDevice);
- } else {
- builder.setScaleFraction(scaleFraction);
- builder.setIsLowResolution(scaleFraction < 1.0f);
- }
-
final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -351,13 +327,23 @@
@Nullable
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
- float scaleFraction) {
- return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
+ TaskSnapshot.Builder builder) {
+ Point taskSize = new Point();
+ final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task,
+ mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize);
+ builder.setTaskSize(taskSize);
+ return taskSnapshot;
}
@Nullable
SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
- float scaleFraction, int pixelFormat) {
+ float scaleFraction) {
+ return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null);
+ }
+
+ @Nullable
+ SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
+ float scaleFraction, int pixelFormat, Point outTaskSize) {
if (task.getSurfaceControl() == null) {
if (DEBUG_SCREENSHOT) {
Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
@@ -369,6 +355,10 @@
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
SurfaceControl.captureLayers(
task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
+ if (outTaskSize != null) {
+ outTaskSize.x = mTmpRect.width();
+ outTaskSize.y = mTmpRect.height();
+ }
final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
: null;
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
@@ -379,21 +369,20 @@
@Nullable
TaskSnapshot snapshotTask(Task task) {
- return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN);
+ return snapshotTask(task, PixelFormat.UNKNOWN);
}
@Nullable
- TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) {
+ TaskSnapshot snapshotTask(Task task, int pixelFormat) {
TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
- if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) {
+ if (!prepareTaskSnapshot(task, pixelFormat, builder)) {
// Failed some pre-req. Has been logged.
return null;
}
final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
- createTaskSnapshot(task, builder.getScaleFraction(),
- builder.getPixelFormat());
+ createTaskSnapshot(task, builder);
if (screenshotBuffer == null) {
// Failed to acquire image. Has been logged.
@@ -472,8 +461,10 @@
final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState());
- final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale);
- final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale);
+ final int taskWidth = task.getBounds().width();
+ final int taskHeight = task.getBounds().height();
+ final int width = (int) (taskWidth * mHighResTaskSnapshotScale);
+ final int height = (int) (taskHeight * mHighResTaskSnapshotScale);
final RenderNode node = RenderNode.create("TaskSnapshotController", null);
node.setLeftTopRightBottom(0, 0, width, height);
@@ -494,9 +485,9 @@
System.currentTimeMillis() /* id */,
topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
- mainWindow.getWindowConfiguration().getRotation(),
- getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */,
- mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
+ mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
+ getInsets(mainWindow), false /* isLowResolution */,
+ false /* isRealSnapshot */, task.getWindowingMode(),
getSystemUiVisibility(task), false);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 01f3427..c20ce5f 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -19,6 +19,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.ComponentName;
import android.graphics.Bitmap;
@@ -26,6 +27,7 @@
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.GraphicBuffer;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
@@ -52,28 +54,110 @@
mPersister = persister;
}
+ static class PreRLegacySnapshotConfig {
+ /**
+ * If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at
+ */
+ final float mScale;
+
+ /**
+ * If {@code true}, always load *_reduced.jpg file, no matter what was requested
+ */
+ final boolean mForceLoadReducedJpeg;
+
+ PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) {
+ mScale = scale;
+ mForceLoadReducedJpeg = forceLoadReducedJpeg;
+ }
+ }
+
+ /**
+ * When device is upgraded, we might be loading a legacy snapshot. In those cases,
+ * restore the scale based on how it was configured historically. See history of
+ * TaskSnapshotPersister for more information.
+ *
+ * | low_ram=false | low_ram=true
+ * +------------------------------------------------------------------------------+
+ * O | *.jpg = 100%, *_reduced.jpg = 50% |
+ * | +-----------------------------------------|
+ * P | | *.jpg = NONE, *_reduced.jpg = 60% |
+ * +------------------------------------+-----------------------------------------+
+ * Q | *.jpg = proto.scale, | *.jpg = NONE, |
+ * | *_reduced.jpg = 50% * proto.scale | *_reduced.jpg = proto.scale |
+ * +------------------------------------+-----------------------------------------+
+ *
+ * @return null if Android R, otherwise a PreRLegacySnapshotConfig object
+ */
+ PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale,
+ boolean highResFileExists, boolean loadLowResolutionBitmap) {
+ float preRLegacyScale = 0;
+ boolean forceLoadReducedJpeg = false;
+ boolean isPreRLegacySnapshot = (taskWidth == 0);
+ if (!isPreRLegacySnapshot) {
+ return null;
+ }
+ final boolean isPreQLegacyProto = isPreRLegacySnapshot
+ && (Float.compare(legacyScale, 0f) == 0);
+
+ if (isPreQLegacyProto) {
+ // Android O or Android P
+ if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) {
+ // Android P w/ low_ram=true
+ preRLegacyScale = 0.6f;
+ // Force bitmapFile to always be *_reduced.jpg
+ forceLoadReducedJpeg = true;
+ } else {
+ // Android O, OR Android P w/ low_ram=false
+ preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f;
+ }
+ } else if (isPreRLegacySnapshot) {
+ // If not pre-Q but is pre-R, then it must be Android Q
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ preRLegacyScale = legacyScale;
+ // Force bitmapFile to always be *_reduced.jpg
+ forceLoadReducedJpeg = true;
+ } else {
+ preRLegacyScale =
+ loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale;
+ }
+ }
+ return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg);
+ }
+
/**
* Loads a task from the disk.
* <p>
* Do not hold the window manager lock when calling this method, as we directly read data from
* disk here, which might be slow.
*
- * @param taskId The id of the task to load.
- * @param userId The id of the user the task belonged to.
- * @param isLowResolution Whether to load a reduced resolution version of the snapshot.
+ * @param taskId The id of the task to load.
+ * @param userId The id of the user the task belonged to.
+ * @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the
+ * snapshot.
* @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
*/
- TaskSnapshot loadTask(int taskId, int userId, boolean isLowResolution) {
+ TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) {
final File protoFile = mPersister.getProtoFile(taskId, userId);
- final File bitmapFile = isLowResolution
- ? mPersister.getLowResolutionBitmapFile(taskId, userId)
- : mPersister.getHighResolutionBitmapFile(taskId, userId);
- if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
+ if (!protoFile.exists()) {
return null;
}
try {
final byte[] bytes = Files.readAllBytes(protoFile.toPath());
final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
+ final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId);
+
+ PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth,
+ proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap);
+
+ boolean forceLoadReducedJpeg =
+ legacyConfig != null && legacyConfig.mForceLoadReducedJpeg;
+ File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg)
+ ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap;
+
+ if (!bitmapFile.exists()) {
+ return null;
+ }
+
final Options options = new Options();
options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
? Config.RGB_565
@@ -99,13 +183,20 @@
final ComponentName topActivityComponent = ComponentName.unflattenFromString(
proto.topActivityComponent);
- // For legacy snapshots, restore the scale based on the reduced resolution state
- final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f;
- final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
- return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(),
- proto.orientation, proto.rotation,
+
+ Point taskSize;
+ if (legacyConfig != null) {
+ int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale);
+ int taskHeight = (int) ((float) hwBitmap.getHeight() / legacyConfig.mScale);
+ taskSize = new Point(taskWidth, taskHeight);
+ } else {
+ taskSize = new Point(proto.taskWidth, proto.taskHeight);
+ }
+
+ return new TaskSnapshot(proto.id, topActivityComponent, buffer,
+ hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
- isLowResolution, scale, proto.isRealSnapshot, proto.windowingMode,
+ loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
proto.systemUiVisibility, proto.isTranslucent);
} catch (IOException e) {
Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 31212b8..164d3e0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -21,8 +21,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.annotation.TestApi;
-import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -52,8 +52,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final String SNAPSHOTS_DIRNAME = "snapshots";
private static final String LOW_RES_FILE_POSTFIX = "_reduced";
- private static final float LOW_RAM_REDUCED_SCALE = .8f;
- static final boolean DISABLE_HIGH_RES_BITMAPS = ActivityManager.isLowRamDeviceStatic();
private static final long DELAY_MS = 100;
private static final int QUALITY = 95;
private static final String PROTO_EXTENSION = ".proto";
@@ -71,7 +69,8 @@
private boolean mStarted;
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
- private final float mLowResScale;
+ private final float mLowResScaleFactor;
+ private boolean mEnableLowResSnapshots;
private final boolean mUse16BitFormat;
/**
@@ -83,13 +82,29 @@
TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver;
+ final float highResTaskSnapshotScale = service.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+ final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
- if (ActivityManager.isLowRamDeviceStatic()) {
- mLowResScale = LOW_RAM_REDUCED_SCALE;
- } else {
- mLowResScale = service.mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
+ if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) {
+ throw new RuntimeException("Low-res scale must be between 0 and 1");
}
+ if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) {
+ throw new RuntimeException("High-res scale must be between 0 and 1");
+ }
+ if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) {
+ throw new RuntimeException("High-res scale must be greater than low-res scale");
+ }
+
+ if (lowResTaskSnapshotScale > 0) {
+ mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
+ setEnableLowResSnapshots(true);
+ } else {
+ mLowResScaleFactor = 0;
+ setEnableLowResSnapshots(false);
+ }
+
mUse16BitFormat = service.mContext.getResources().getBoolean(
com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
}
@@ -155,13 +170,16 @@
}
}
+ boolean enableLowResSnapshots() {
+ return mEnableLowResSnapshots;
+ }
+
/**
- * Gets the scaling the persister uses for low resolution task snapshots.
- *
- * @return the lowResBitmap scale of task snapshots when they are set to be low res
+ * Not to be used. Only here for testing.
*/
- float getLowResScale() {
- return mLowResScale;
+ @VisibleForTesting
+ void setEnableLowResSnapshots(boolean enabled) {
+ mEnableLowResSnapshots = enabled;
}
/**
@@ -213,14 +231,10 @@
}
File getHighResolutionBitmapFile(int taskId, int userId) {
- // Full sized bitmaps are disabled on low ram devices
- if (DISABLE_HIGH_RES_BITMAPS) {
- Slog.wtf(TAG, "This device does not support full sized resolution bitmaps.");
- return null;
- }
return new File(getDirectory(userId), taskId + BITMAP_EXTENSION);
}
+ @NonNull
File getLowResolutionBitmapFile(int taskId, int userId) {
return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION);
}
@@ -234,11 +248,11 @@
final File protoFile = getProtoFile(taskId, userId);
final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
protoFile.delete();
- bitmapLowResFile.delete();
-
- // Low ram devices do not have a full sized file to delete
- if (!DISABLE_HIGH_RES_BITMAPS) {
- final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
+ if (bitmapLowResFile.exists()) {
+ bitmapLowResFile.delete();
+ }
+ final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
+ if (bitmapFile.exists()) {
bitmapFile.delete();
}
}
@@ -343,6 +357,8 @@
final TaskSnapshotProto proto = new TaskSnapshotProto();
proto.orientation = mSnapshot.getOrientation();
proto.rotation = mSnapshot.getRotation();
+ proto.taskWidth = mSnapshot.getTaskSize().x;
+ proto.taskHeight = mSnapshot.getTaskSize().y;
proto.insetLeft = mSnapshot.getContentInsets().left;
proto.insetTop = mSnapshot.getContentInsets().top;
proto.insetRight = mSnapshot.getContentInsets().right;
@@ -352,7 +368,6 @@
proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
proto.isTranslucent = mSnapshot.isTranslucent();
proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
- proto.scale = mSnapshot.getScale();
proto.id = mSnapshot.getId();
final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
final File file = getProtoFile(mTaskId, mUserId);
@@ -379,11 +394,26 @@
}
final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
- final Bitmap lowResBitmap = mSnapshot.isLowResolution()
- ? swBitmap
- : Bitmap.createScaledBitmap(swBitmap,
- (int) (bitmap.getWidth() * mLowResScale),
- (int) (bitmap.getHeight() * mLowResScale), true /* filter */);
+
+ final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
+ try {
+ FileOutputStream fos = new FileOutputStream(file);
+ swBitmap.compress(JPEG, QUALITY, fos);
+ fos.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
+ return false;
+ }
+
+ if (!enableLowResSnapshots()) {
+ swBitmap.recycle();
+ return true;
+ }
+
+ final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap,
+ (int) (bitmap.getWidth() * mLowResScaleFactor),
+ (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */);
+ swBitmap.recycle();
final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId);
try {
@@ -396,22 +426,6 @@
}
lowResBitmap.recycle();
- // For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps
- if (mSnapshot.isLowResolution()) {
- swBitmap.recycle();
- return true;
- }
-
- final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
- try {
- FileOutputStream fos = new FileOutputStream(file);
- swBitmap.compress(JPEG, QUALITY, fos);
- fos.close();
- } catch (IOException e) {
- Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
- return false;
- }
- swBitmap.recycle();
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index f4e4245..eb005e0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -36,6 +36,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -53,9 +54,11 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -131,6 +134,8 @@
private final Rect mContentInsets = new Rect();
private final Rect mFrame = new Rect();
private TaskSnapshot mSnapshot;
+ private final RectF mTmpSnapshotSize = new RectF();
+ private final RectF mTmpDstFrame = new RectF();
private final CharSequence mTitle;
private boolean mHasDrawn;
private long mShownTime;
@@ -141,6 +146,8 @@
@VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private final int mOrientationOnCreation;
private final SurfaceControl.Transaction mTransaction;
+ private final Matrix mSnapshotMatrix = new Matrix();
+ private final float[] mTmpFloat9 = new float[9];
static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
TaskSnapshot snapshot) {
@@ -365,13 +372,17 @@
frame = calculateSnapshotFrame(crop);
mTransaction.setWindowCrop(mChildSurfaceControl, crop);
mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+ mTmpDstFrame.set(frame);
} else {
frame = null;
+ mTmpDstFrame.set(mFrame);
}
// Scale the mismatch dimensions to fill the task bounds
- final float scale = 1 / mSnapshot.getScale();
- mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale);
+ mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
+ mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
+ mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
+
mTransaction.apply();
surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
surface.release();
@@ -395,13 +406,17 @@
rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
final Rect insets = mSnapshot.getContentInsets();
+ final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
+ final float scaleY =
+ (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
+
// Let's remove all system decorations except the status bar, but only if the task is at the
// very top of the screen.
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
- rect.inset((int) (insets.left * mSnapshot.getScale()),
- isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
- (int) (insets.right * mSnapshot.getScale()),
- (int) (insets.bottom * mSnapshot.getScale()));
+ rect.inset((int) (insets.left * scaleX),
+ isTop ? 0 : (int) (insets.top * scaleY),
+ (int) (insets.right * scaleX),
+ (int) (insets.bottom * scaleY));
return rect;
}
@@ -412,14 +427,20 @@
*/
@VisibleForTesting
Rect calculateSnapshotFrame(Rect crop) {
- final Rect frame = new Rect(crop);
- final float scale = mSnapshot.getScale();
+ final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
+ final float scaleY =
+ (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
// Rescale the frame from snapshot to window coordinate space
- frame.scale(1 / scale);
+ final Rect frame = new Rect(
+ (int) (crop.left / scaleX + 0.5f),
+ (int) (crop.top / scaleY + 0.5f),
+ (int) (crop.right / scaleX + 0.5f),
+ (int) (crop.bottom / scaleY + 0.5f)
+ );
// By default, offset it to to top/left corner
- frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
+ frame.offsetTo((int) (-crop.left / scaleX), (int) (-crop.top / scaleY));
// However, we also need to make space for the navigation bar on the left side.
final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 137d122..669eb78 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -669,8 +669,7 @@
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps,
- ArraySet<ActivityRecord> changingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<ActivityRecord> openingApps) {
boolean adjust = false;
if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
@@ -682,15 +681,6 @@
break;
}
}
- if (!adjust) {
- for (int i = changingApps.size() - 1; i >= 0; --i) {
- final ActivityRecord activity = changingApps.valueAt(i);
- if (activity.windowsCanBeWallpaperTarget()) {
- adjust = true;
- break;
- }
- }
- }
}
if (adjust) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7a4d0b0..a0a70dc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -25,9 +25,13 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -64,6 +68,7 @@
import android.view.DisplayInfo;
import android.view.IWindowContainer;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
@@ -94,7 +99,7 @@
* changes are made to this class.
*/
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
- implements Comparable<WindowContainer>, Animatable,
+ implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
BLASTSyncEngine.TransactionReadyListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
@@ -169,6 +174,7 @@
* Applied as part of the animation pass in "prepareSurfaces".
*/
protected final SurfaceAnimator mSurfaceAnimator;
+ final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
private final Point mTmpPos = new Point();
@@ -252,7 +258,6 @@
* where it represents the starting-state snapshot.
*/
WindowContainerThumbnail mThumbnail;
- final Rect mTransitStartRect = new Rect();
final Point mTmpPoint = new Point();
protected final Rect mTmpRect = new Rect();
final Rect mTmpPrevBounds = new Rect();
@@ -277,6 +282,7 @@
mWmService = wms;
mPendingTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
+ mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
@Override
@@ -524,13 +530,6 @@
if (mSurfaceControl != null) {
getPendingTransaction().remove(mSurfaceControl);
-
- // Merge to parent transaction to ensure the transactions on this WindowContainer are
- // applied in native even if WindowContainer is removed.
- if (mParent != null) {
- mParent.getPendingTransaction().merge(getPendingTransaction());
- }
-
setSurfaceControl(null);
mLastSurfacePosition.set(0, 0);
scheduleAnimation();
@@ -841,7 +840,7 @@
* @return {@code true} if the container is in changing app transition.
*/
boolean isChangingAppTransition() {
- return false;
+ return mDisplayContent != null && mDisplayContent.mChangingContainers.contains(this);
}
void sendAppVisibilityToClients() {
@@ -893,6 +892,31 @@
}
/**
+ * Called when the visibility of a child is asked to change. This is before visibility actually
+ * changes (eg. a transition animation might play out first).
+ */
+ void onChildVisibilityRequested(boolean visible) {
+ // If we are changing visibility, then a snapshot isn't necessary and we are no-longer
+ // part of a change transition.
+ mSurfaceFreezer.unfreeze(getPendingTransaction());
+ if (mDisplayContent != null) {
+ mDisplayContent.mChangingContainers.remove(this);
+ }
+ WindowContainer parent = getParent();
+ if (parent != null) {
+ parent.onChildVisibilityRequested(visible);
+ }
+ }
+
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(USER_ID, USER_NULL);
+ proto.write(TITLE, "WindowContainer");
+ proto.end(token);
+ }
+
+ /**
* Returns {@code true} if this container is focusable. Generally, if a parent is not focusable,
* this will not be focusable either.
*/
@@ -1924,7 +1948,8 @@
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
- mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback);
+ mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
+ mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -1941,6 +1966,11 @@
}
@Override
+ public SurfaceControl getFreezeSnapshotTarget() {
+ return null;
+ }
+
+ @Override
public Builder makeAnimationLeash() {
return makeSurface().setContainerLayer();
}
@@ -2009,8 +2039,9 @@
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
- mThumbnail.startAnimation(
- getPendingTransaction(), thumbnailAdapter, !isVisible());
+ mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
+ thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION,
+ (type, anim) -> { });
}
}
} else {
@@ -2056,7 +2087,7 @@
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(this, mTmpPoint, mTmpRect,
- (isChanging ? mTransitStartRect : null));
+ (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
} else if (isChanging) {
final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -2064,14 +2095,15 @@
mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(mTransitStartRect, mTmpRect, displayInfo,
- durationScale, true /* isAppAnimation */, false /* isThumbnail */),
+ new WindowChangeAnimationSpec(mSurfaceFreezer.mFreezeBounds, mTmpRect,
+ displayInfo, durationScale, true /* isAppAnimation */,
+ false /* isThumbnail */),
getSurfaceAnimationRunner());
- final AnimationAdapter thumbnailAdapter = mThumbnail != null
- ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(mTransitStartRect,
- mTmpRect, displayInfo, durationScale, true /* isAppAnimation */,
- true /* isThumbnail */), getSurfaceAnimationRunner())
+ final AnimationAdapter thumbnailAdapter = mSurfaceFreezer.mSnapshot != null
+ ? new LocalAnimationAdapter(new WindowChangeAnimationSpec(
+ mSurfaceFreezer.mFreezeBounds, mTmpRect, displayInfo, durationScale,
+ true /* isAppAnimation */, true /* isThumbnail */), getSurfaceAnimationRunner())
: null;
resultAdapters = new Pair<>(adapter, thumbnailAdapter);
mTransit = transit;
@@ -2189,6 +2221,7 @@
@Override
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
+ mSurfaceFreezer.unfreeze(t);
reassignLayer(t);
}
@@ -2329,6 +2362,10 @@
mSurfaceControl = sc;
}
+ RemoteAnimationDefinition getRemoteAnimationDefinition() {
+ return null;
+ }
+
/** Cheap way of doing cast and instanceof. */
Task asTask() {
return null;
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 35d245f..3e4c992 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.LocusId;
+import android.os.FileUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -251,5 +252,6 @@
void onDestroy() {
mEventStore.onDestroy();
mConversationStore.onDestroy();
+ FileUtils.deleteContentsAndDir(mPackageDataDir);
}
}
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index d2f86ee..602e4e1 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -26,6 +26,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index ef0ca66..5160eae 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -28,6 +28,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index feae1e1..08bd1ee 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -144,4 +144,49 @@
Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
+
+ @Test
+ public void testBrightnessHasLowerPriorityThanUser() {
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+ assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+
+ int displayId = 0;
+ DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(displayId, votes);
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+
+ votes.clear();
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+ director.injectVotesByDisplay(votesByDisplay);
+ desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+
+
+ }
}
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 30ab9cd..dc30add 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
@@ -117,7 +117,7 @@
@SmallTest
public void testGetBatterySaverPolicy_PolicyVibration_WithAccessibilityEnabled() {
- mBatterySaverPolicy.setAccessibilityEnabledForTest(true);
+ mBatterySaverPolicy.setAccessibilityEnabled(true);
testServiceDefaultValue_Off(ServiceType.VIBRATION);
}
@@ -339,4 +339,57 @@
Policy.fromSettings(BATTERY_SAVER_CONSTANTS, ""));
verifyBatterySaverConstantsUpdated();
}
+
+ public void testCarModeChanges_Full() {
+ mBatterySaverPolicy.updateConstantsLocked(
+ "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ + ",enable_night_mode=true", "");
+ mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(true);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE,
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
+ assertFalse(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(false);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+ }
+
+ public void testCarModeChanges_Adaptive() {
+ mBatterySaverPolicy.setAdaptivePolicyLocked(
+ Policy.fromSettings(
+ "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+ + ",enable_night_mode=true", ""));
+ mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE);
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(true);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE,
+ PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
+ assertFalse(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+ mBatterySaverPolicy.setCarModeEnabled(false);
+
+ assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+ .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+ assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+ ServiceType.NIGHT_MODE).batterySaverEnabled);
+ }
}
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index dab0a5f..767857b 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -23,7 +23,6 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d0283f7..29b0df5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -153,7 +153,6 @@
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
-import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
@@ -360,7 +359,7 @@
@Before
public void setUp() throws Exception {
// Shell permisssions will override permissions of our app, so add all necessary permissions
- // fo this test here:
+ // for this test here:
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
"android.permission.WRITE_DEVICE_CONFIG",
"android.permission.READ_DEVICE_CONFIG",
@@ -6089,7 +6088,9 @@
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- shortcutInfos.add(mock(ShortcutInfo.class));
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.isLongLived()).thenReturn(true);
+ shortcutInfos.add(info);
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
// Test: Send the bubble notification
@@ -6116,7 +6117,8 @@
verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
// We're no longer a bubble
- Notification notif2 = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
+ Notification notif2 = mService.getNotificationRecord(
+ nr.getSbn().getKey()).getNotification();
assertFalse(notif2.isBubbleNotification());
}
@@ -6409,11 +6411,6 @@
convos.add(convo2);
when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
- // only one valid shortcut
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
- .setPackage(PKG_P)
- .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
- .setShortcutIds(Arrays.asList(channel1.getConversationId()));
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getShortLabel()).thenReturn("Hello");
when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 2d4b5a7..00b9273 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -51,6 +51,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -203,16 +204,13 @@
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
- private StatusBarNotification getMessagingStyleNotification(@Nullable String shortcutId) {
+ private StatusBarNotification getMessagingStyleNotification() {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
Person person = new Person.Builder().setName("Bob").build();
builder.setStyle(new Notification.MessagingStyle(person));
- if (shortcutId != null) {
- builder.setShortcutId(shortcutId);
- }
Notification n = builder.build();
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
@@ -1122,16 +1120,18 @@
@Test
public void testIsConversation() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
assertTrue(record.isConversation());
}
@Test
- public void testIsConversation_nullShortcutId() {
- StatusBarNotification sbn = getMessagingStyleNotification(null);
+ public void testIsConversation_nullShortcut() {
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
assertFalse(record.isConversation());
}
@@ -1140,25 +1140,28 @@
public void testIsConversation_bypassShortcutFlagEnabled() {
Settings.Global.putString(mContentResolver,
FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
- StatusBarNotification sbn = getMessagingStyleNotification(null);
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
assertTrue(record.isConversation());
}
@Test
public void testIsConversation_channelDemoted() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
channel.setDemoted(true);
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
assertFalse(record.isConversation());
}
@Test
public void testIsConversation_withAdjustmentOverride() {
- StatusBarNotification sbn = getMessagingStyleNotification("test_shortcut_id");
+ StatusBarNotification sbn = getMessagingStyleNotification();
NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(mock(ShortcutInfo.class));
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_NOT_CONVERSATION, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 27f72a1..e4d50c0 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -55,6 +55,8 @@
import android.util.AtomicFile;
import android.util.Pair;
+import androidx.test.InstrumentationRegistry;
+
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.server.LocalServices;
@@ -65,6 +67,7 @@
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -116,6 +119,10 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ // Shell permisssions will override permissions of our app, so add all necessary permissions
+ // for this test here:
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ "android.permission.READ_CONTACTS");
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
@@ -153,6 +160,12 @@
mService.setPreferencesHelper(mPreferencesHelper);
}
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().dropShellPermissionIdentity();
+ }
+
@Test
public void testInit() throws Exception {
List<String> dialer0 = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 7204a81..e1ce431f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -255,7 +255,7 @@
}
private void notifyTransitionStarting(ActivityRecord activity) {
- final ArrayMap<ActivityRecord, Integer> reasons = new ArrayMap<>();
+ final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
reasons.put(activity, ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN);
mActivityMetricsLogger.notifyTransitionStarting(reasons);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 8c2b293..4cb50c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -103,12 +103,12 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
// Verify we are in a change transition, but without a snapshot.
// Though, the test will actually have crashed by now if a snapshot is attempted.
- assertNull(mActivity.getThumbnail());
- assertTrue(mActivity.isInChangeTransition());
+ assertNull(mTask.mSurfaceFreezer.mSnapshot);
+ assertTrue(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -121,14 +121,14 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mActivity.isInChangeTransition());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
+ assertTrue(mTask.isInChangeTransition());
// Removing the app-token from the display should clean-up the
// the change leash.
mDisplayContent.removeAppToken(mActivity.token);
- assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mActivity.isInChangeTransition());
+ assertEquals(0, mDisplayContent.mChangingContainers.size());
+ assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -152,8 +152,8 @@
assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode());
// Make sure we're not waiting for a change animation (no leash)
- assertFalse(mActivity.isInChangeTransition());
- assertNull(mActivity.getThumbnail());
+ assertFalse(mTask.isInChangeTransition());
+ assertNull(mActivity.mSurfaceFreezer.mSnapshot);
waitUntilHandlersIdle();
mActivity.removeImmediately();
@@ -165,13 +165,13 @@
setUpOnDisplay(mDisplayContent);
mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
- assertEquals(1, mDisplayContent.mChangingApps.size());
- assertTrue(mActivity.isInChangeTransition());
+ assertEquals(1, mDisplayContent.mChangingContainers.size());
+ assertTrue(mTask.isInChangeTransition());
// Changing visibility should cancel the change transition and become closing
mActivity.setVisibility(false, false);
- assertEquals(0, mDisplayContent.mChangingApps.size());
- assertFalse(mActivity.isInChangeTransition());
+ assertEquals(0, mDisplayContent.mChangingContainers.size());
+ assertFalse(mTask.isInChangeTransition());
waitUntilHandlersIdle();
mActivity.removeImmediately();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 3a724a1..c7f94ef 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -247,7 +247,7 @@
@Test
public void testChange() throws Exception {
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
- mDisplayContent.mChangingApps.add(win.mActivityRecord);
+ mDisplayContent.mChangingContainers.add(win.mActivityRecord);
try {
final RemoteAnimationRecord record = mController.createRemoteAnimationRecord(
win.mActivityRecord, new Point(50, 100), new Rect(50, 100, 150, 150),
@@ -290,7 +290,7 @@
verify(mThumbnailFinishedCallback).onAnimationFinished(
eq(ANIMATION_TYPE_WINDOW_ANIMATION), eq(record.mThumbnailAdapter));
} finally {
- mDisplayContent.mChangingApps.clear();
+ mDisplayContent.mChangingContainers.clear();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index ec20262..71d3194 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -52,7 +52,6 @@
import static org.mockito.ArgumentMatchers.refEq;
import android.app.ActivityOptions;
-import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -517,7 +516,7 @@
*/
@Test
public void testStartHomeOnAllDisplays() {
- mockResolveHomeActivity();
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
mockResolveSecondaryHomeActivity();
// Create secondary displays.
@@ -644,30 +643,26 @@
}
/**
- * Tests that secondary home should be selected if default home not set.
+ * Tests that secondary home should be selected if primary home not set.
*/
@Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() {
- final Intent defaultHomeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = ResolverActivity.class.getName();
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(defaultHomeIntent));
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() {
+ // Setup: primary home not set.
+ final Intent primaryHomeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoPrimary = new ActivityInfo();
+ aInfoPrimary.name = ResolverActivity.class.getName();
+ doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(primaryHomeIntent));
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
- final String secondaryHomeComponent = mService.mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = comp.getClassName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
-
- // Should fallback to secondary home if default home not set.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
}
/**
@@ -675,103 +670,60 @@
* config_useSystemProvidedLauncherForSecondary.
*/
@Test
- public void testResolveSecondaryHomeActivityForced() throws Exception {
- Resources resources = mContext.getResources();
- spyOn(resources);
- try {
- // setUp: set secondary launcher and force it.
- final String defaultSecondaryHome =
- "com.android.test/com.android.test.TestDefaultSecondaryHome";
- final ComponentName secondaryComp = ComponentName.unflattenFromString(
- defaultSecondaryHome);
- doReturn(defaultSecondaryHome).when(resources).getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- doReturn(true).when(resources).getBoolean(
- com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = secondaryComp.getClassName();
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(secondaryHomeIntent));
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
- // Let resolveActivities call to validate both main launcher and second launcher so that
- // resolveActivities call does not work as enabler for secondary.
- final List<ResolveInfo> resolutions1 = new ArrayList<>();
- final ResolveInfo resolveInfo1 = new ResolveInfo();
- resolveInfo1.activityInfo = new ActivityInfo();
- resolveInfo1.activityInfo.name = aInfoDefault.name;
- resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
- resolutions1.add(resolveInfo1);
- doReturn(resolutions1).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(homeIntent));
- final List<ResolveInfo> resolutions2 = new ArrayList<>();
- final ResolveInfo resolveInfo2 = new ResolveInfo();
- resolveInfo2.activityInfo = new ActivityInfo();
- resolveInfo2.activityInfo.name = aInfoSecondary.name;
- resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
- resolutions2.add(resolveInfo2);
- doReturn(resolutions2).when(mRootWindowContainer).resolveActivities(anyInt(),
- refEq(secondaryHomeIntent));
- doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
- any(), anyInt(), anyBoolean());
-
- // Run the test
- final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
- .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
- assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
- assertEquals(secondaryComp.getPackageName(),
- resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
- } finally {
- // tearDown
- reset(resources);
- }
- }
-
- /**
- * Tests that secondary home should be selected if default home not support secondary displays
- * or there is no matched activity in the same package as selected default home.
- */
- @Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() {
- mockResolveHomeActivity();
-
+ public void testResolveSecondaryHomeActivityForced() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: set secondary home and force it.
+ mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */);
+ final Intent secondaryHomeIntent =
+ mService.getSecondaryHomeIntent(null /* preferredPackage */);
final List<ResolveInfo> resolutions = new ArrayList<>();
- doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
-
- final String secondaryHomeComponent = mService.mContext.getResources().getString(
- com.android.internal.R.string.config_secondaryHomeComponent);
- final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent);
- final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = comp.getClassName();
- doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ resolveInfo.activityInfo = aInfoSecondary;
+ resolutions.add(resolveInfo);
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(),
refEq(secondaryHomeIntent));
+ doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
- // Should fallback to secondary home if selected default home not support secondary displays
- // or there is no matched activity in the same package as selected default home.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(comp.getClassName(), resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
}
/**
- * Tests that default home activity should be selected if it already support secondary displays.
+ * Tests that secondary home should be selected if primary home not support secondary displays
+ * or there is no matched activity in the same package as selected primary home.
*/
@Test
- public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() {
- final ActivityInfo aInfoDefault = mockResolveHomeActivity();
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() {
+ // Setup: there is no matched activity in the same package as selected primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ final List<ResolveInfo> resolutions = new ArrayList<>();
+ doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
+ // Setup: set secondary home.
+ mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */);
+ // Run the test.
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ assertEquals(aInfoSecondary.applicationInfo.packageName,
+ resolvedInfo.first.applicationInfo.packageName);
+ }
+ /**
+ * Tests that primary home activity should be selected if it already support secondary displays.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() {
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // SetUp: put primary home info on 2nd item
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
infoFake1.activityInfo = new ActivityInfo();
@@ -779,7 +731,8 @@
infoFake1.activityInfo.applicationInfo = new ApplicationInfo();
infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1";
final ResolveInfo infoFake2 = new ResolveInfo();
- infoFake2.activityInfo = aInfoDefault;
+ final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */);
+ infoFake2.activityInfo = aInfoPrimary;
resolutions.add(infoFake1);
resolutions.add(infoFake2);
doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any());
@@ -787,13 +740,12 @@
doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
any(), anyInt(), anyBoolean());
- // Use default home activity if it support secondary displays.
+ // Run the test.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
-
- assertEquals(aInfoDefault.applicationInfo.packageName,
+ assertEquals(aInfoPrimary.name, resolvedInfo.first.name);
+ assertEquals(aInfoPrimary.applicationInfo.packageName,
resolvedInfo.first.applicationInfo.packageName);
- assertEquals(aInfoDefault.name, resolvedInfo.first.name);
}
/**
@@ -801,8 +753,9 @@
*/
@Test
public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() {
- mockResolveHomeActivity();
-
+ // SetUp: set primary home.
+ mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */);
+ // Setup: prepare two eligible activity info.
final List<ResolveInfo> resolutions = new ArrayList<>();
final ResolveInfo infoFake1 = new ResolveInfo();
infoFake1.activityInfo = new ActivityInfo();
@@ -821,7 +774,7 @@
doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay(
any(), anyInt(), anyBoolean());
- // Use the first one of matched activities in the same package as selected default home.
+ // Use the first one of matched activities in the same package as selected primary home.
final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer
.resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
@@ -884,32 +837,48 @@
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
- * info for test cases (the original implementation will resolve from the real package manager).
+ * info for test cases.
+ *
+ * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use
+ * secondary home intent.
+ * @param forceSystemProvided Indicate to force using system provided home activity.
*/
- private ActivityInfo mockResolveHomeActivity() {
- final Intent homeIntent = mService.getHomeIntent();
- final ActivityInfo aInfoDefault = new ActivityInfo();
- aInfoDefault.name = "fakeHomeActivity";
- aInfoDefault.applicationInfo = new ApplicationInfo();
- aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
- doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
- refEq(homeIntent));
- return aInfoDefault;
+ private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) {
+ ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome);
+ Intent targetIntent;
+ if (primaryHome) {
+ targetIntent = mService.getHomeIntent();
+ } else {
+ Resources resources = mContext.getResources();
+ spyOn(resources);
+ doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString(
+ com.android.internal.R.string.config_secondaryHomePackage);
+ doReturn(forceSystemProvided).when(resources).getBoolean(
+ com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+ targetIntent = mService.getSecondaryHomeIntent(null /* preferredPackage */);
+ }
+ doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(),
+ refEq(targetIntent));
}
/**
* Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent
- * activity info for test cases (the original implementation will resolve from the real package
- * manager).
+ * activity info for test cases.
*/
private void mockResolveSecondaryHomeActivity() {
final Intent secondaryHomeIntent = mService
.getSecondaryHomeIntent(null /* preferredPackage */);
- final ActivityInfo aInfoSecondary = new ActivityInfo();
- aInfoSecondary.name = "fakeSecondaryHomeActivity";
- aInfoSecondary.applicationInfo = new ApplicationInfo();
- aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage";
+ final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false);
doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer)
.resolveSecondaryHomeActivity(anyInt(), anyInt());
}
+
+ private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) {
+ final ActivityInfo aInfo = new ActivityInfo();
+ aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity";
+ aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.packageName =
+ primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage";
+ return aInfo;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index bd8aacb..20d9aff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -25,6 +25,7 @@
import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -36,6 +37,7 @@
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
@@ -138,6 +140,7 @@
final int orientation = Configuration.ORIENTATION_PORTRAIT;
final float scaleFraction = 0.25f;
final Rect contentInsets = new Rect(1, 2, 3, 4);
+ final Point taskSize = new Point(5, 6);
try {
ActivityManager.TaskSnapshot.Builder builder =
@@ -147,14 +150,13 @@
builder.setSystemUiVisibility(systemUiVisibility);
builder.setWindowingMode(windowingMode);
builder.setColorSpace(sRGB);
- builder.setIsLowResolution(true);
builder.setOrientation(orientation);
builder.setContentInsets(contentInsets);
builder.setIsTranslucent(true);
- builder.setScaleFraction(0.25f);
builder.setSnapshot(buffer);
builder.setIsRealSnapshot(true);
builder.setPixelFormat(pixelFormat);
+ builder.setTaskSize(taskSize);
// Not part of TaskSnapshot itself, used in screenshot process
assertEquals(pixelFormat, builder.getPixelFormat());
@@ -165,13 +167,15 @@
assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
assertEquals(windowingMode, snapshot.getWindowingMode());
assertEquals(sRGB, snapshot.getColorSpace());
- assertTrue(snapshot.isLowResolution());
+ // Snapshots created with the Builder class are always high-res. The only way to get a
+ // low-res snapshot is to load it from the disk in TaskSnapshotLoader.
+ assertFalse(snapshot.isLowResolution());
assertEquals(orientation, snapshot.getOrientation());
assertEquals(contentInsets, snapshot.getContentInsets());
assertTrue(snapshot.isTranslucent());
- assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f);
assertSame(buffer, snapshot.getSnapshot());
assertTrue(snapshot.isRealSnapshot());
+ assertEquals(taskSize, snapshot.getTaskSize());
} finally {
if (buffer != null) {
buffer.destroy();
@@ -188,11 +192,9 @@
final ActivityManager.TaskSnapshot.Builder builder =
new ActivityManager.TaskSnapshot.Builder();
- final float scaleFraction = 0.8f;
mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(),
- scaleFraction, PixelFormat.UNKNOWN, builder);
+ PixelFormat.UNKNOWN, builder);
- assertEquals(scaleFraction, builder.getScaleFraction(), 0 /* delta */);
// The pixel format should be selected automatically.
assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 0b16e5c..40f15b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -19,12 +19,16 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -36,10 +40,12 @@
import androidx.test.filters.MediumTest;
+import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig;
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
import java.io.File;
import java.util.function.Predicate;
@@ -55,6 +61,8 @@
@RunWith(WindowTestRunner.class)
public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
+ private static final float DELTA = 0.00001f;
+
private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
@Test
@@ -148,29 +156,172 @@
}
@Test
- public void testLowResolutionPersistAndLoadSnapshot() {
+ public void testLegacyPLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any P low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0f;
+ final boolean hasHighResFile = false;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, 0.6f, DELTA);
+ assertTrue(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, 0.6f, DELTA);
+ assertTrue(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testLegacyPNonLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any O device, or a P non-low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0f;
+ final boolean hasHighResFile = true;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, 1.0f, DELTA);
+ assertFalse(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, 0.5f, DELTA);
+ assertFalse(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testLegacyQLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any Q low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0.6f;
+ final boolean hasHighResFile = false;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, legacyScale, DELTA);
+ assertEquals(highResConf.mScale, 0.6f, DELTA);
+ assertTrue(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, legacyScale, DELTA);
+ assertEquals(lowResConf.mScale, 0.6f, DELTA);
+ assertTrue(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testLegacyQNonLowRamConfig() throws Exception {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class)
+ .startMocking();
+
+ when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false);
+
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any Q non-low_ram device
+ final int taskWidth = 0;
+ final float legacyScale = 0.8f;
+ final boolean hasHighResFile = true;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNotNull(highResConf);
+ assertEquals(highResConf.mScale, legacyScale, DELTA);
+ assertEquals(highResConf.mScale, 0.8f, DELTA);
+ assertFalse(highResConf.mForceLoadReducedJpeg);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNotNull(lowResConf);
+ assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA);
+ assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA);
+ assertFalse(lowResConf.mForceLoadReducedJpeg);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testNonLegacyRConfig() throws Exception {
+ // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+ // for any R device
+ final int taskWidth = 1440;
+ final float legacyScale = 0f;
+ final boolean hasHighResFile = true;
+
+ PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+ assertNull(highResConf);
+
+ PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+ taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+ assertNull(lowResConf);
+ }
+
+ @Test
+ public void testDisabledLowResolutionPersistAndLoadSnapshot() {
+ mPersister.setEnableLowResSnapshots(false);
+
TaskSnapshot a = new TaskSnapshotBuilder()
- .setScale(0.5f)
+ .setScaleFraction(0.5f)
.setIsLowResolution(true)
.build();
assertTrue(a.isLowResolution());
mPersister.persistSnapshot(1, mTestUserId, a);
mPersister.waitForQueueEmpty();
final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
- new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+ new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
final File[] nonExistsFiles = new File[]{
- new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+ new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
};
assertTrueForFiles(files, File::exists, " must exist");
assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
- final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* isLowResolution */);
+ final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
assertNotNull(snapshot);
assertEquals(TEST_INSETS, snapshot.getContentInsets());
assertNotNull(snapshot.getSnapshot());
assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
- false /* isLowResolution */);
+ true /* isLowResolution */);
assertNull(snapshotNotExist);
}
@@ -271,13 +422,11 @@
@Test
public void testScalePersistAndLoadSnapshot() {
TaskSnapshot a = new TaskSnapshotBuilder()
- .setScale(0.25f)
+ .setScaleFraction(0.25f)
.build();
TaskSnapshot b = new TaskSnapshotBuilder()
- .setScale(0.75f)
+ .setScaleFraction(0.75f)
.build();
- assertEquals(0.25f, a.getScale(), 1E-5);
- assertEquals(0.75f, b.getScale(), 1E-5);
mPersister.persistSnapshot(1, mTestUserId, a);
mPersister.persistSnapshot(2, mTestUserId, b);
mPersister.waitForQueueEmpty();
@@ -287,8 +436,6 @@
false /* isLowResolution */);
assertNotNull(snapshotA);
assertNotNull(snapshotB);
- assertEquals(0.25f, snapshotA.getScale(), 1E-5);
- assertEquals(0.75f, snapshotB.getScale(), 1E-5);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 4612dba..fa6663c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -30,6 +30,7 @@
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.UserManager;
import android.view.Surface;
@@ -87,8 +88,10 @@
* Builds a TaskSnapshot.
*/
static class TaskSnapshotBuilder {
+ private static final int SNAPSHOT_WIDTH = 100;
+ private static final int SNAPSHOT_HEIGHT = 100;
- private float mScale = 1f;
+ private float mScaleFraction = 1f;
private boolean mIsLowResolution = false;
private boolean mIsRealSnapshot = true;
private boolean mIsTranslucent = false;
@@ -96,8 +99,11 @@
private int mSystemUiVisibility = 0;
private int mRotation = Surface.ROTATION_0;
- TaskSnapshotBuilder setScale(float scale) {
- mScale = scale;
+ TaskSnapshotBuilder() {
+ }
+
+ TaskSnapshotBuilder setScaleFraction(float scale) {
+ mScaleFraction = scale;
return this;
}
@@ -132,15 +138,20 @@
}
TaskSnapshot build() {
- final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
+ // To satisfy existing tests, ensure the graphics buffer is always 100x100, and
+ // compute the ize of the task according to mScaleFraction.
+ Point taskSize = new Point((int) (SNAPSHOT_WIDTH / mScaleFraction),
+ (int) (SNAPSHOT_HEIGHT / mScaleFraction));
+ final GraphicBuffer buffer = GraphicBuffer.create(SNAPSHOT_WIDTH, SNAPSHOT_HEIGHT,
+ PixelFormat.RGBA_8888,
USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
Canvas c = buffer.lockCanvas();
c.drawColor(Color.RED);
buffer.unlockCanvasAndPost(c);
return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- mRotation, TEST_INSETS,
- mIsLowResolution, mScale, mIsRealSnapshot,
+ mRotation, taskSize, TEST_INSETS,
+ mIsLowResolution, mIsRealSnapshot,
mWindowingMode, mSystemUiVisibility, mIsTranslucent);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index bb0e5ae..2164de9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -38,6 +38,7 @@
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.Surface;
@@ -67,12 +68,22 @@
int windowFlags, Rect taskBounds) {
final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
+
+ // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
+ // this behavior set the taskSize to be the same as the taskBounds width and height. The
+ // taskBounds passed here are assumed to be the same task bounds as when the snapshot was
+ // taken. We assume there is no aspect ratio mismatch between the screenshot and the
+ // taskBounds
+ assertEquals(width, taskBounds.width());
+ assertEquals(height, taskBounds.height());
+ Point taskSize = new Point(taskBounds.width(), taskBounds.height());
+
final TaskSnapshot snapshot = new TaskSnapshot(
System.currentTimeMillis(),
new ComponentName("", ""), buffer,
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
- Surface.ROTATION_0, contentInsets, false,
- 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+ Surface.ROTATION_0, taskSize, contentInsets, false,
+ true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
0 /* systemUiVisibility */, false /* isTranslucent */);
mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
@@ -152,7 +163,7 @@
@Test
public void testCalculateSnapshotCrop_taskNotOnTop() {
- setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100));
+ setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9d77623..6a40487 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4096,7 +4096,8 @@
/** Prefix of all Wifi.KEY_* constants. */
public static final String KEY_PREFIX = "wifi.";
/**
- * It contains the maximum client count definition that the carrier owns.
+ * It contains the maximum client count definition that the carrier sets.
+ * The default is 0, which means that the carrier hasn't set a requirement.
*/
public static final String KEY_HOTSPOT_MAX_CLIENT_COUNT =
KEY_PREFIX + "hotspot_maximum_client_count";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0660776..a36df49 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2788,39 +2788,22 @@
/**
* Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
* registered operator or the cell nearby, if available.
- * <p>
- * The ISO-3166 country code is provided in lowercase 2 character format.
- * <p>
- * Note: In multi-sim, this returns a shared emergency network country iso from other
- * subscription if the subscription used to create the TelephonyManager doesn't camp on
- * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
- * slot.
+ *
* Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
* if on a CDMA network).
* <p>
* @return the lowercase 2 character ISO-3166 country code, or empty string if not available.
*/
public String getNetworkCountryIso() {
- try {
- ITelephony telephony = getITelephony();
- if (telephony == null) return "";
- return telephony.getNetworkCountryIsoForPhone(getPhoneId(),
- null /* no permission check */, null);
- } catch (RemoteException ex) {
- return "";
- }
+ return getNetworkCountryIso(getSlotIndex());
}
/**
* Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
- * registered operator or the cell nearby, if available.
- * <p>
- * The ISO-3166 country code is provided in lowercase 2 character format.
- * <p>
- * Note: In multi-sim, this returns a shared emergency network country iso from other
- * subscription if the subscription used to create the TelephonyManager doesn't camp on
- * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
- * slot.
+ * registered operator or the cell nearby, if available. This is same as
+ * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for
+ * accessing network country info from the SIM slot that does not have SIM inserted.
+ *
* Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
* if on a CDMA network).
* <p>
@@ -2831,22 +2814,18 @@
*
* @throws IllegalArgumentException when the slotIndex is invalid.
*
- * {@hide}
*/
- @SystemApi
- @TestApi
@NonNull
- @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getNetworkCountryIso(int slotIndex) {
try {
- if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+ if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX
+ && !SubscriptionManager.isValidSlotIndex(slotIndex)) {
throw new IllegalArgumentException("invalid slot index " + slotIndex);
}
ITelephony telephony = getITelephony();
if (telephony == null) return "";
- return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(),
- getFeatureId());
+ return telephony.getNetworkCountryIsoForPhone(slotIndex);
} catch (RemoteException ex) {
return "";
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 861925f..af5089f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -281,7 +281,7 @@
* operator's MCC (Mobile Country Code).
* @see android.telephony.TelephonyManager#getNetworkCountryIso
*/
- String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId);
+ String getNetworkCountryIsoForPhone(int phoneId);
/**
* Returns the neighboring cell information of the device.
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 1c8b6be..61f3dba 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -30,6 +30,7 @@
private ITestDevice mTestDevice;
private static final String SYSTEM_SERVER_PROFILE =
"/data/misc/profiles/cur/0/android/primary.prof";
+ private static final boolean USE_PHENOTYPE = false;
@Override
public void setDevice(ITestDevice testDevice) {
@@ -41,16 +42,33 @@
return mTestDevice;
}
+ private String getProperty(String property) throws Exception {
+ if (USE_PHENOTYPE) {
+ return mTestDevice.getProperty("persist.device_config.runtime_native_boot."
+ + property);
+ } else {
+ return mTestDevice.executeShellCommand("getprop dalvik.vm." + property).trim();
+ }
+ }
+
+ private String setProperty(String property, String value) throws Exception {
+ if (USE_PHENOTYPE) {
+ return mTestDevice.executeShellCommand(
+ "device_config put runtime_native_boot " + property + " " + value);
+ } else {
+ return mTestDevice.executeShellCommand(
+ "setprop dalvik.vm." + property + " " + value);
+ }
+ }
+
/**
* Validate that the boot image profile properties are set.
*/
public void validateProperties() throws Exception {
- String res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilebootclasspath");
- assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilesystemserver");
- assertTrue("profile system server not enabled", res != null && res.equals("true"));
+ String res = getProperty("profilebootclasspath");
+ assertTrue("profile boot class path not enabled: " + res, "true".equals(res));
+ res = getProperty("profilesystemserver");
+ assertTrue("profile system server not enabled: " + res, "true".equals(res));
}
private boolean forceSaveProfile(String pkg) throws Exception {
@@ -67,33 +85,48 @@
@Test
public void testSystemServerProfile() throws Exception {
final int numIterations = 20;
+ String res;
+ // Set properties and wait for them to be readable.
for (int i = 1; i <= numIterations; ++i) {
- String res;
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilebootclasspath");
- boolean profileBootClassPath = res != null && res.equals("true");
- res = mTestDevice.getProperty(
- "persist.device_config.runtime_native_boot.profilesystemserver");
- boolean profileSystemServer = res != null && res.equals("true");
+ String pbcp = getProperty("profilebootclasspath");
+ boolean profileBootClassPath = "true".equals(pbcp);
+ String pss = getProperty("profilesystemserver");
+ boolean profileSystemServer = "true".equals(pss);
if (profileBootClassPath && profileSystemServer) {
break;
}
if (i == numIterations) {
- assertTrue("profile system server not enabled", profileSystemServer);
- assertTrue("profile boot class path not enabled", profileSystemServer);
+ assertTrue("profile system server not enabled: " + pss, profileSystemServer);
+ assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
}
- res = mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot profilebootclasspath true");
- res = mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot profilesystemserver true");
- res = mTestDevice.executeShellCommand("stop");
- res = mTestDevice.executeShellCommand("start");
- Thread.sleep(5000);
+ setProperty("profilebootclasspath", "true");
+ setProperty("profilesystemserver", "true");
+ Thread.sleep(1000);
}
+
+ // Restart shell and wait for system boot.
+ res = mTestDevice.executeShellCommand("stop");
+ assertTrue("stop shell: " + res, res.length() == 0);
+ res = mTestDevice.executeShellCommand("start");
+ assertTrue("start shell: " + res, res.length() == 0);
+ for (int i = 1; i <= numIterations; ++i) {
+ String pbcp = getProperty("profilebootclasspath");
+ boolean profileBootClassPath = "true".equals(pbcp);
+ String pss = getProperty("profilesystemserver");
+ boolean profileSystemServer = "true".equals(pss);
+ if (profileBootClassPath && profileSystemServer) {
+ break;
+ }
+ if (i == numIterations) {
+ assertTrue("profile system server not enabled: " + pss, profileSystemServer);
+ assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ }
+ Thread.sleep(1000);
+ }
+
// Trunacte the profile before force it to be saved to prevent previous profiles
// causing the test to pass.
- String res;
res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
assertTrue(res, res.length() == 0);
// Wait up to 20 seconds for the profile to be saved.