Merge "Persist the one-time permission flag if set" 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/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/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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f2ec938..fa751d3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7853,11 +7853,16 @@
/**
* Returns if the provided drawable represents the default activity icon provided by the system.
*
- * PackageManager provides a default icon for any package/activity if the app itself does not
- * define one or if the system encountered any error when loading the icon.
+ * PackageManager silently returns a default application icon for any package/activity if the
+ * app itself does not define one or if the system encountered any error when loading the icon.
+ *
+ * Developers can use this to check implement app specific logic around retrying or caching.
*
* @return true if the drawable represents the default activity icon, false otherwise
* @see #getDefaultActivityIcon()
+ * @see PackageItemInfo#loadDefaultIcon(PackageManager)
+ * @see #getActivityIcon
+ * @see LauncherActivityInfo#getIcon(int)
*/
public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) {
int resId = drawable instanceof AdaptiveIconDrawable
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..fb9158f 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. -->
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/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/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/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/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/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/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index a2a08502..bb665fe 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -67,6 +67,7 @@
internal const val CONTROLS_AVAILABLE = "systemui.controls_available"
internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE)
private const val USER_CHANGE_RETRY_DELAY = 500L // ms
+ private const val DEFAULT_ENABLED = 1
}
// Map of map: ComponentName -> (String -> ControlInfo).
@@ -79,7 +80,8 @@
private val contentResolver: ContentResolver
get() = context.contentResolver
- override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0
+ override var available = Settings.Secure.getInt(
+ contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED) != 0
private set
private var currentUser = context.user
@@ -104,7 +106,7 @@
userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
persistenceWrapper.changeFile(fileName)
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
- /* default */ 0, newUser.identifier) != 0
+ /* default */ DEFAULT_ENABLED, newUser.identifier) != 0
synchronized(currentFavorites) {
currentFavorites.clear()
}
@@ -140,7 +142,7 @@
return
}
available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
- /* default */ 0, currentUserId) != 0
+ /* default */ DEFAULT_ENABLED, currentUserId) != 0
synchronized(currentFavorites) {
currentFavorites.clear()
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 4fba83a..5da02dc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -92,6 +92,7 @@
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -189,6 +190,8 @@
private ControlsUiController mControlsUiController;
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
+ private final ControlsListingController mControlsListingController;
+ private boolean mAnyControlsProviders = false;
/**
* @param context everything needs a context :(
@@ -208,7 +211,8 @@
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
- @Background Executor backgroundExecutor) {
+ @Background Executor backgroundExecutor,
+ ControlsListingController controlsListingController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -232,6 +236,7 @@
mControlsUiController = controlsUiController;
mIWindowManager = iWindowManager;
mBackgroundExecutor = backgroundExecutor;
+ mControlsListingController = controlsListingController;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -268,6 +273,8 @@
}
}
});
+
+ mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty());
}
/**
@@ -1914,6 +1921,7 @@
private boolean shouldShowControls() {
return mKeyguardStateController.isUnlocked()
- && mControlsUiController.getAvailable();
+ && mControlsUiController.getAvailable()
+ && mAnyControlsProviders;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index fb89ed2..bfac85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -37,8 +37,8 @@
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.qs.QS;
@@ -86,6 +86,7 @@
private int mY;
private boolean mOpening;
private boolean mIsShowingNavBackdrop;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
@Inject
public QSCustomizer(Context context, AttributeSet attrs,
@@ -187,7 +188,7 @@
int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
mX = x - containerLocation[0];
mY = y - containerLocation[1];
- MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);
isShown = true;
mOpening = true;
setTileSpecs();
@@ -224,7 +225,7 @@
public void hide() {
final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
if (isShown) {
- MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
isShown = false;
mToolbar.dismissPopupMenus();
setCustomizing(false);
@@ -258,7 +259,7 @@
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MENU_RESET:
- MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET);
reset();
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt
new file mode 100644
index 0000000..ff8ddec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.qs.customize
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Tile removed from current tiles")
+ QS_EDIT_REMOVE(210),
+ @UiEvent(doc = "Tile added to current tiles")
+ QS_EDIT_ADD(211),
+ @UiEvent(doc = "Tile moved")
+ QS_EDIT_MOVE(212),
+ @UiEvent(doc = "QS customizer open")
+ QS_EDIT_OPEN(213),
+ @UiEvent(doc = "QS customizer closed")
+ QS_EDIT_CLOSED(214),
+ @UiEvent(doc = "QS tiles reset")
+ QS_EDIT_RESET(215);
+
+ override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 3afc460..58de95d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -40,8 +40,8 @@
import androidx.recyclerview.widget.RecyclerView.State;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.customize.TileAdapter.Holder;
@@ -92,6 +92,7 @@
private int mAccessibilityFromIndex;
private CharSequence mAccessibilityFromLabel;
private QSTileHost mHost;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
public TileAdapter(Context context) {
mContext = context;
@@ -436,20 +437,11 @@
move(from, to, mTiles);
updateDividerLocations();
if (to >= mEditIndex) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC,
- strip(mTiles.get(to)));
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE,
- from);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to)));
} else if (from >= mEditIndex) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC,
- strip(mTiles.get(to)));
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD,
- to);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_ADD, 0, strip(mTiles.get(to)));
} else {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC,
- strip(mTiles.get(to)));
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE,
- to);
+ mUiEventLogger.log(QSEditEvent.QS_EDIT_MOVE, 0, strip(mTiles.get(to)));
}
saveSpecs(mHost);
return true;
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/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/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..b78d024 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -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/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7f98c7f..8ebbce3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -36,6 +36,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 +89,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 +180,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.
*/
@@ -1455,7 +1462,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 +1513,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 +1538,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
@@ -3136,9 +3348,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);
}
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 475f229..c9c6d51 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -380,7 +380,7 @@
private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
- private static final long DELAY_FOR_ASSISTANT_TIME = 100;
+ private static final long DELAY_FOR_ASSISTANT_TIME = 200;
private static final String ACTION_NOTIFICATION_TIMEOUT =
NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8ccb59f..e468810 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2712,6 +2712,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();
}
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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7a4d0b0..aaaabf2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -524,13 +524,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();
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/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/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/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.