Merge "Add Content PiP support with launch-into-pip API"
diff --git a/Android.bp b/Android.bp
index ee5db70..1c4f10e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,7 +110,7 @@
// AIDL sources from external directories
":android.hardware.graphics.common-V3-java-source",
- ":android.hardware.security.keymint-V1-java-source",
+ ":android.hardware.security.keymint-V2-java-source",
":android.hardware.security.secureclock-V1-java-source",
":android.hardware.tv.tuner-V1-java-source",
":android.security.apc-java-source",
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 273f59c..5d76b08 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2507,9 +2507,10 @@
package android.app.usage {
public final class BroadcastResponseStats implements android.os.Parcelable {
- ctor public BroadcastResponseStats(@NonNull String);
+ ctor public BroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
method public int describeContents();
method @IntRange(from=0) public int getBroadcastsDispatchedCount();
+ method @IntRange(from=1) public long getId();
method @IntRange(from=0) public int getNotificationsCancelledCount();
method @IntRange(from=0) public int getNotificationsPostedCount();
method @IntRange(from=0) public int getNotificationsUpdatedCount();
@@ -2566,13 +2567,13 @@
}
public final class UsageStatsManager {
- method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
+ method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void clearBroadcastResponseStats(@Nullable String, @IntRange(from=0) long);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
method @RequiresPermission(allOf={android.Manifest.permission.INTERACT_ACROSS_USERS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long getLastTimeAnyComponentUsed(@NonNull String);
method public int getUsageSource();
method @RequiresPermission(android.Manifest.permission.BIND_CARRIER_SERVICES) public void onCarrierPrivilegedAppsChanged();
- method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public android.app.usage.BroadcastResponseStats queryBroadcastResponseStats(@NonNull String, @IntRange(from=1) long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.List<android.app.usage.BroadcastResponseStats> queryBroadcastResponseStats(@Nullable String, @IntRange(from=0) long);
method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @Nullable android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], @NonNull java.time.Duration, @NonNull java.time.Duration, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
@@ -6075,7 +6076,7 @@
method public boolean isAudioServerRunning();
method public boolean isHdmiSystemAudioSupported();
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
- method public static boolean isUltrasoundSupported();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public boolean isUltrasoundSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void muteAwaitConnection(@NonNull int[], @NonNull android.media.AudioDeviceAttributes, long, @NonNull java.util.concurrent.TimeUnit) throws java.lang.IllegalStateException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
diff --git a/core/java/android/app/usage/BroadcastResponseStats.java b/core/java/android/app/usage/BroadcastResponseStats.java
index 5acc3dda..e1d37e1 100644
--- a/core/java/android/app/usage/BroadcastResponseStats.java
+++ b/core/java/android/app/usage/BroadcastResponseStats.java
@@ -23,6 +23,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Class containing a collection of stats related to response events started from an app
* after receiving a broadcast.
@@ -32,17 +34,30 @@
@SystemApi
public final class BroadcastResponseStats implements Parcelable {
private final String mPackageName;
+ private final long mId;
private int mBroadcastsDispatchedCount;
private int mNotificationsPostedCount;
private int mNotificationsUpdatedCount;
private int mNotificationsCancelledCount;
- public BroadcastResponseStats(@NonNull String packageName) {
+ /**
+ * Creates a new {@link BroadcastResponseStats} object that contain the stats for broadcasts
+ * with {@code id} (specified using
+ * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)} by the sender) that
+ * were sent to {@code packageName}.
+ *
+ * @param packageName the name of the package that broadcasts were sent to.
+ * @param id the ID specified by the sender using
+ * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+ */
+ public BroadcastResponseStats(@NonNull String packageName, @IntRange(from = 1) long id) {
mPackageName = packageName;
+ mId = id;
}
private BroadcastResponseStats(@NonNull Parcel in) {
mPackageName = in.readString8();
+ mId = in.readLong();
mBroadcastsDispatchedCount = in.readInt();
mNotificationsPostedCount = in.readInt();
mNotificationsUpdatedCount = in.readInt();
@@ -58,6 +73,14 @@
}
/**
+ * @return the ID of the broadcasts that the stats in this object correspond to.
+ */
+ @IntRange(from = 1)
+ public long getId() {
+ return mId;
+ }
+
+ /**
* Returns the total number of broadcasts that were dispatched to the app by the caller.
*
* <b> Note that the returned count will only include the broadcasts that the caller explicitly
@@ -148,9 +171,35 @@
}
@Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || !(obj instanceof BroadcastResponseStats)) {
+ return false;
+ }
+ final BroadcastResponseStats other = (BroadcastResponseStats) obj;
+ return this.mBroadcastsDispatchedCount == other.mBroadcastsDispatchedCount
+ && this.mNotificationsPostedCount == other.mNotificationsPostedCount
+ && this.mNotificationsUpdatedCount == other.mNotificationsUpdatedCount
+ && this.mNotificationsCancelledCount == other.mNotificationsCancelledCount
+ && this.mId == other.mId
+ && this.mPackageName.equals(other.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mId, mBroadcastsDispatchedCount,
+ mNotificationsPostedCount, mNotificationsUpdatedCount,
+ mNotificationsCancelledCount);
+ }
+
+ @Override
public @NonNull String toString() {
return "stats {"
- + "broadcastsSent=" + mBroadcastsDispatchedCount
+ + "package=" + mPackageName
+ + ",id=" + mId
+ + ",broadcastsSent=" + mBroadcastsDispatchedCount
+ ",notificationsPosted=" + mNotificationsPostedCount
+ ",notificationsUpdated=" + mNotificationsUpdatedCount
+ ",notificationsCancelled=" + mNotificationsCancelledCount
@@ -165,6 +214,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
dest.writeString8(mPackageName);
+ dest.writeLong(mId);
dest.writeInt(mBroadcastsDispatchedCount);
dest.writeInt(mNotificationsPostedCount);
dest.writeInt(mNotificationsUpdatedCount);
diff --git a/core/java/android/app/usage/BroadcastResponseStatsList.aidl b/core/java/android/app/usage/BroadcastResponseStatsList.aidl
new file mode 100644
index 0000000..7380359
--- /dev/null
+++ b/core/java/android/app/usage/BroadcastResponseStatsList.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+/** {@hide} */
+parcelable BroadcastResponseStatsList;
\ No newline at end of file
diff --git a/core/java/android/app/usage/BroadcastResponseStatsList.java b/core/java/android/app/usage/BroadcastResponseStatsList.java
new file mode 100644
index 0000000..4d2ff286
--- /dev/null
+++ b/core/java/android/app/usage/BroadcastResponseStatsList.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public final class BroadcastResponseStatsList implements Parcelable {
+ private List<BroadcastResponseStats> mBroadcastResponseStats;
+
+ public BroadcastResponseStatsList(
+ @NonNull List<BroadcastResponseStats> broadcastResponseStats) {
+ mBroadcastResponseStats = broadcastResponseStats;
+ }
+
+ private BroadcastResponseStatsList(@NonNull Parcel in) {
+ mBroadcastResponseStats = new ArrayList<>();
+ final byte[] bytes = in.readBlob();
+ final Parcel data = Parcel.obtain();
+ try {
+ data.unmarshall(bytes, 0, bytes.length);
+ data.setDataPosition(0);
+ data.readTypedList(mBroadcastResponseStats, BroadcastResponseStats.CREATOR);
+ } finally {
+ data.recycle();
+ }
+ }
+
+ @NonNull
+ public List<BroadcastResponseStats> getList() {
+ return mBroadcastResponseStats == null ? Collections.emptyList() : mBroadcastResponseStats;
+ }
+
+ @Override
+ public @ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
+ final Parcel data = Parcel.obtain();
+ try {
+ data.writeTypedList(mBroadcastResponseStats);
+ dest.writeBlob(data.marshall());
+ } finally {
+ data.recycle();
+ }
+ }
+
+ public static final @NonNull Creator<BroadcastResponseStatsList> CREATOR =
+ new Creator<BroadcastResponseStatsList>() {
+ @Override
+ public @NonNull BroadcastResponseStatsList createFromParcel(
+ @NonNull Parcel source) {
+ return new BroadcastResponseStatsList(source);
+ }
+
+ @Override
+ public @NonNull BroadcastResponseStatsList[] newArray(int size) {
+ return new BroadcastResponseStatsList[size];
+ }
+ };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 6f8fea1..a430714 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.app.usage.BroadcastResponseStats;
+import android.app.usage.BroadcastResponseStatsList;
import android.app.usage.UsageEvents;
import android.content.pm.ParceledListSlice;
@@ -73,9 +74,11 @@
void forceUsageSourceSettingRead();
long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
- BroadcastResponseStats queryBroadcastResponseStats(
+ BroadcastResponseStatsList queryBroadcastResponseStats(
String packageName, long id, String callingPackage, int userId);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
void clearBroadcastResponseStats(String packageName, long id, String callingPackage,
int userId);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)")
+ void clearBroadcastEvents(String callingPackage, int userId);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index b81c62d..d7152b3 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1397,29 +1397,46 @@
* Returns the broadcast response stats since the last boot corresponding to
* {@code packageName} and {@code id}.
*
- * <p>Broadcast response stats will include the aggregated data of what actions an app took upon
- * receiving a broadcast. This data will consider the broadcasts that the caller sent to
+ * <p> Broadcast response stats will include the aggregated data of what actions an app took
+ * upon receiving a broadcast. This data will consider the broadcasts that the caller sent to
* {@code packageName} and explicitly requested to record the response events using
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
*
- * @param packageName The name of the package that the caller wants to query for.
- * @param id The ID corresponding to the broadcasts that the caller wants to query for. This is
- * the ID the caller specifies when requesting a broadcast response event to be
- * recorded using {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+ * <p> The returned list could one or more {@link BroadcastResponseStats} objects or be empty
+ * depending on the {@code packageName} and {@code id} and whether there is any data
+ * corresponding to these. If the {@code packageName} is not {@code null} and {@code id} is
+ * {@code > 0}, then the returned list would contain at most one {@link BroadcastResponseStats}
+ * object. Otherwise, the returned list could contain more than one
+ * {@link BroadcastResponseStats} object in no particular order.
*
- * @return the broadcast response stats corresponding to {@code packageName} and {@code id}.
+ * <p> Note: It is possible that same {@code id} was used for broadcasts sent to different
+ * packages. So, callers can query the data corresponding to
+ * all broadcasts with a particular {@code id} by passing {@code packageName} as {@code null}.
*
+ * @param packageName The name of the package that the caller wants to query for
+ * or {@code null} to indicate that data corresponding to all packages
+ * should be returned.
+ * @param id The ID corresponding to the broadcasts that the caller wants to query for, or
+ * {@code 0} to indicate that data corresponding to all IDs should be returned.
+ * This is the ID the caller specifies when requesting a broadcast response event
+ * to be recorded using
+ * {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
+ *
+ * @return the list of broadcast response stats corresponding to {@code packageName}
+ * and {@code id}.
+ *
+ * @see #clearBroadcastResponseStats(String, long)
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@UserHandleAware
@NonNull
- public BroadcastResponseStats queryBroadcastResponseStats(
- @NonNull String packageName, @IntRange(from = 1) long id) {
+ public List<BroadcastResponseStats> queryBroadcastResponseStats(
+ @Nullable String packageName, @IntRange(from = 0) long id) {
try {
return mService.queryBroadcastResponseStats(packageName, id,
- mContext.getOpPackageName(), mContext.getUserId());
+ mContext.getOpPackageName(), mContext.getUserId()).getList();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1428,12 +1445,15 @@
/**
* Clears the broadcast response stats corresponding to {@code packageName} and {@code id}.
*
- * When a caller uses this API, stats related to the events occurring till that point will be
- * cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
+ * <p> When a caller uses this API, stats related to the events occurring till that point will
+ * be cleared and subsequent calls to {@link #queryBroadcastResponseStats(String, long)} will
* return stats related to events occurring after this.
*
- * @param packageName The name of the package that the caller wants to clear the data for.
- * @param id The ID corresponding to the broadcasts that the caller wants to clear the data for.
+ * @param packageName The name of the package that the caller wants to clear the data for or
+ * {@code null} to indicate that data corresponding to all packages should
+ * be cleared.
+ * @param id The ID corresponding to the broadcasts that the caller wants to clear the data
+ * for, or {code 0} to indicate that data corresponding to all IDs should be deleted.
* This is the ID the caller specifies when requesting a broadcast response event
* to be recorded using
* {@link BroadcastOptions#recordResponseEventWhileInBackground(long)}.
@@ -1444,8 +1464,8 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
@UserHandleAware
- public void clearBroadcastResponseStats(@NonNull String packageName,
- @IntRange(from = 1) long id) {
+ public void clearBroadcastResponseStats(@Nullable String packageName,
+ @IntRange(from = 0) long id) {
try {
mService.clearBroadcastResponseStats(packageName, id,
mContext.getOpPackageName(), mContext.getUserId());
@@ -1453,4 +1473,19 @@
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Clears the broadcast events that were sent by the caller uid.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+ @UserHandleAware
+ public void clearBroadcastEvents() {
+ try {
+ mService.clearBroadcastEvents(mContext.getOpPackageName(), mContext.getUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 179f6ee..4dc1fca 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11785,7 +11785,10 @@
* user and that ideally it should not be covered. Setting this is only appropriate for UI
* where the user would likely take action to uncover it.
* <p>
- * The system will try to respect this, but when not possible will ignore it.
+ * The system will try to respect this preference, but when not possible will ignore it.
+ * <p>
+ * Note: while this is set to {@code true}, the system will ignore the {@code Rect}s provided
+ * through {@link #setPreferKeepClearRects} (but not clear them).
* <p>
* @see #setPreferKeepClearRects
* @see #isPreferKeepClear
@@ -11817,11 +11820,11 @@
* user and that ideally they should not be covered. Setting this is only appropriate for UI
* where the user would likely take action to uncover it.
* <p>
- * If the whole view is preferred to be clear ({@link #isPreferKeepClear}), the rects set here
- * will be ignored.
- * <p>
* The system will try to respect this preference, but when not possible will ignore it.
* <p>
+ * Note: While {@link #isPreferKeepClear} is {@code true}, the {@code Rect}s set here are
+ * ignored.
+ * <p>
* @see #setPreferKeepClear
* @see #getPreferKeepClearRects
*/
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 0443ad0..0835824 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -455,7 +455,14 @@
if (mSourceU != null) {
// T done
mResultT = (T) res;
- mSourceU.whenComplete(this);
+
+ // Subscribe to the second job completion.
+ mSourceU.whenComplete((r, e) -> {
+ // Mark the first job completion by setting mSourceU to null, so that next time
+ // the execution flow goes to the else case below.
+ mSourceU = null;
+ accept(r, e);
+ });
} else {
// U done
try {
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
index c758504..f3af528 100644
--- a/core/java/com/android/internal/util/PerfettoTrigger.java
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -18,6 +18,7 @@
import android.os.SystemClock;
import android.util.Log;
+import android.util.SparseLongArray;
import java.io.IOException;
@@ -28,8 +29,9 @@
public class PerfettoTrigger {
private static final String TAG = "PerfettoTrigger";
private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
- private static final long THROTTLE_MILLIS = 60000;
- private static volatile long sLastTriggerTime = -THROTTLE_MILLIS;
+ private static final long THROTTLE_MILLIS = 300000;
+ private static final SparseLongArray sLastInvocationPerTrigger = new SparseLongArray(100);
+ private static final Object sLock = new Object();
/**
* @param triggerName The name of the trigger. Must match the value defined in the AOT
@@ -38,18 +40,23 @@
public static void trigger(String triggerName) {
// Trace triggering has a non-negligible cost (fork+exec).
// To mitigate potential excessive triggering by the API client we ignore calls that happen
- // too quickl after the most recent trigger.
- long sinceLastTrigger = SystemClock.elapsedRealtime() - sLastTriggerTime;
- if (sinceLastTrigger < THROTTLE_MILLIS) {
- Log.v(TAG, "Not triggering " + triggerName + " - not enough time since last trigger");
- return;
+ // too quickly after the most recent trigger.
+ synchronized (sLock) {
+ long lastTrigger = sLastInvocationPerTrigger.get(triggerName.hashCode());
+ long sinceLastTrigger = SystemClock.elapsedRealtime() - lastTrigger;
+ if (sinceLastTrigger < THROTTLE_MILLIS) {
+ Log.v(TAG, "Not triggering " + triggerName
+ + " - not enough time since last trigger");
+ return;
+ }
+
+ sLastInvocationPerTrigger.put(triggerName.hashCode(), SystemClock.elapsedRealtime());
}
try {
ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
pb.start();
- sLastTriggerTime = SystemClock.elapsedRealtime();
} catch (IOException e) {
Log.w(TAG, "Failed to trigger " + triggerName, e);
}
diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java
index 17b84ff..d9c1e57 100644
--- a/core/java/com/android/internal/util/UserIcons.java
+++ b/core/java/com/android/internal/util/UserIcons.java
@@ -46,11 +46,21 @@
* Converts a given drawable to a bitmap.
*/
public static Bitmap convertToBitmap(Drawable icon) {
+ return convertToBitmapAtSize(icon, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+ }
+
+ /**
+ * Converts a given drawable to a bitmap, with width and height equal to the default icon size.
+ */
+ public static Bitmap convertToBitmapAtUserIconSize(Resources res, Drawable icon) {
+ int size = res.getDimensionPixelSize(R.dimen.user_icon_size);
+ return convertToBitmapAtSize(icon, size, size);
+ }
+
+ private static Bitmap convertToBitmapAtSize(Drawable icon, int width, int height) {
if (icon == null) {
return null;
}
- final int width = icon.getIntrinsicWidth();
- final int height = icon.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
icon.setBounds(0, 0, width, height);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 78bb53d..5fa4a65 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -150,6 +150,7 @@
private Icon mShortcutIcon;
private View mAppNameDivider;
private TouchDelegateComposite mTouchDelegate = new TouchDelegateComposite(this);
+ private ArrayList<MessagingGroup> mToRecycle = new ArrayList<>();
public ConversationLayout(@NonNull Context context) {
super(context);
@@ -472,6 +473,12 @@
updateTitleAndNamesDisplay();
updateConversationLayout();
+
+ // Recycle everything at the end of the update, now that we know it's no longer needed.
+ for (MessagingGroup group : mToRecycle) {
+ group.recycle();
+ }
+ mToRecycle.clear();
}
/**
@@ -745,18 +752,18 @@
MessagingGroup group = oldGroups.get(i);
if (!mGroups.contains(group)) {
List<MessagingMessage> messages = group.getMessages();
- Runnable endRunnable = () -> {
- mMessagingLinearLayout.removeTransientView(group);
- group.recycle();
- };
-
boolean wasShown = group.isShown();
mMessagingLinearLayout.removeView(group);
if (wasShown && !MessagingLinearLayout.isGone(group)) {
mMessagingLinearLayout.addTransientView(group, 0);
- group.removeGroupAnimated(endRunnable);
+ group.removeGroupAnimated(() -> {
+ mMessagingLinearLayout.removeTransientView(group);
+ group.recycle();
+ });
} else {
- endRunnable.run();
+ // Defer recycling until after the update is done, since we may still need the
+ // old group around to perform other updates.
+ mToRecycle.add(group);
}
mMessages.removeAll(messages);
mHistoricMessages.removeAll(messages);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 47cb754..4aa00f6 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -64,7 +64,6 @@
"libbase",
"libcutils",
"libharfbuzz_ng",
- "libhwui",
"liblog",
"libminikin",
"libz",
@@ -266,6 +265,7 @@
"libui",
"libgraphicsenv",
"libgui",
+ "libhwui",
"libmediandk",
"libpermission",
"libsensor",
@@ -344,9 +344,21 @@
],
static_libs: [
"libandroidfw",
- "libcompiler_rt",
- "libutils",
+ "libbinary_parse",
+ "libdng_sdk",
+ "libft2",
"libhostgraphics",
+ "libhwui",
+ "libimage_type_recognition",
+ "libjpeg",
+ "libpiex",
+ "libpng",
+ "libtiff_directory",
+ "libui-types",
+ "libutils",
+ "libwebp-decode",
+ "libwebp-encode",
+ "libwuffs_mirror_release_c",
],
},
linux_glibc: {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 3e513df..93ba23b 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -14,15 +14,31 @@
* limitations under the License.
*/
-#include "jni.h"
-#include "core_jni_helpers.h"
-
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/graphics/jni_runtime.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/jni_macros.h>
#include <unicode/putil.h>
+#include <unicode/udata.h>
+
#include <clocale>
#include <sstream>
#include <unordered_map>
#include <vector>
+#include "core_jni_helpers.h"
+#include "jni.h"
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#endif
+
+#include <iostream>
+
using namespace std;
/*
@@ -33,6 +49,33 @@
*/
static JavaVM* javaVM;
+static jclass bridge;
+static jclass layoutLog;
+static jmethodID getLogId;
+static jmethodID logMethodId;
+
+extern int register_android_os_Binder(JNIEnv* env);
+extern int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env);
+
+typedef void (*FreeFunction)(void*);
+
+static void NativeAllocationRegistry_Delegate_nativeApplyFreeFunction(JNIEnv*, jclass,
+ jlong freeFunction,
+ jlong ptr) {
+ void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+ FreeFunction nativeFreeFunction =
+ reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+ nativeFreeFunction(nativePtr);
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(NativeAllocationRegistry_Delegate, nativeApplyFreeFunction, "(JJ)V"),
+};
+
+int register_libcore_util_NativeAllocationRegistry_Delegate(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry_Delegate", gMethods,
+ NELEM(gMethods));
+}
namespace android {
@@ -47,6 +90,7 @@
extern int register_android_database_SQLiteDebug(JNIEnv* env);
extern int register_android_os_FileObserver(JNIEnv* env);
extern int register_android_os_MessageQueue(JNIEnv* env);
+extern int register_android_os_Parcel(JNIEnv* env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
@@ -54,6 +98,11 @@
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
+extern int register_android_view_KeyCharacterMap(JNIEnv* env);
+extern int register_android_view_KeyEvent(JNIEnv* env);
+extern int register_android_view_MotionEvent(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
+extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
#define REG_JNI(name) { name }
@@ -78,8 +127,10 @@
{"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
{"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
#ifdef __linux__
+ {"android.os.Binder", REG_JNI(register_android_os_Binder)},
{"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
{"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+ {"android.os.Parcel", REG_JNI(register_android_os_Parcel)},
#endif
{"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
{"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
@@ -88,11 +139,15 @@
{"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
{"android.util.Log", REG_JNI(register_android_util_Log)},
{"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)},
+ {"android.view.KeyCharacterMap", REG_JNI(register_android_view_KeyCharacterMap)},
+ {"android.view.KeyEvent", REG_JNI(register_android_view_KeyEvent)},
+ {"android.view.MotionEvent", REG_JNI(register_android_view_MotionEvent)},
+ {"android.view.VelocityTracker", REG_JNI(register_android_view_VelocityTracker)},
{"com.android.internal.util.VirtualRefBasePtr",
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
+ {"libcore.util.NativeAllocationRegistry_Delegate",
+ REG_JNI(register_libcore_util_NativeAllocationRegistry_Delegate)},
};
-// Vector to store the names of classes that need delegates of their native methods
-static vector<string> classesToDelegate;
static int register_jni_procs(const std::unordered_map<std::string, RegJNIRec>& jniRegMap,
const vector<string>& classesToRegister, JNIEnv* env) {
@@ -102,36 +157,17 @@
return -1;
}
}
+
+ if (register_android_graphics_classes(env) < 0) {
+ return -1;
+ }
+
return 0;
}
int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods) {
- string classNameString = string(className);
- if (find(classesToDelegate.begin(), classesToDelegate.end(), classNameString)
- != classesToDelegate.end()) {
- // Register native methods to the delegate class <classNameString>_NativeDelegate
- // by adding _Original to the name of each method.
- replace(classNameString.begin(), classNameString.end(), '$', '_');
- string delegateClassName = classNameString + "_NativeDelegate";
- jclass clazz = env->FindClass(delegateClassName.c_str());
- JNINativeMethod gTypefaceDelegateMethods[numMethods];
- for (int i = 0; i < numMethods; i++) {
- JNINativeMethod gTypefaceMethod = gMethods[i];
- string newName = string(gTypefaceMethod.name) + "_Original";
- gTypefaceDelegateMethods[i].name = strdup(newName.c_str());
- gTypefaceDelegateMethods[i].signature = gTypefaceMethod.signature;
- gTypefaceDelegateMethods[i].fnPtr = gTypefaceMethod.fnPtr;
- }
- int result = env->RegisterNatives(clazz, gTypefaceDelegateMethods, numMethods);
- for (int i = 0; i < numMethods; i++) {
- free((char*)gTypefaceDelegateMethods[i].name);
- }
- return result;
- }
-
- jclass clazz = env->FindClass(className);
- return env->RegisterNatives(clazz, gMethods, numMethods);
+ return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
JNIEnv* AndroidRuntime::getJNIEnv() {
@@ -164,6 +200,125 @@
return result;
}
+void LayoutlibLogger(base::LogId, base::LogSeverity severity, const char* tag, const char* file,
+ unsigned int line, const char* message) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint logPrio = severity;
+ jstring tagString = env->NewStringUTF(tag);
+ jstring messageString = env->NewStringUTF(message);
+
+ jobject bridgeLog = env->CallStaticObjectMethod(bridge, getLogId);
+
+ env->CallVoidMethod(bridgeLog, logMethodId, logPrio, tagString, messageString);
+
+ env->DeleteLocalRef(tagString);
+ env->DeleteLocalRef(messageString);
+ env->DeleteLocalRef(bridgeLog);
+}
+
+void LayoutlibAborter(const char* abort_message) {
+ // Layoutlib should not call abort() as it would terminate Studio.
+ // Throw an exception back to Java instead.
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jniThrowRuntimeException(env, "The Android framework has encountered a fatal error");
+}
+
+// This method has been copied/adapted from system/core/init/property_service.cpp
+// If the ro.product.cpu.abilist* properties have not been explicitly
+// set, derive them from ro.system.product.cpu.abilist* properties.
+static void property_initialize_ro_cpu_abilist() {
+ const std::string EMPTY = "";
+ const char* kAbilistProp = "ro.product.cpu.abilist";
+ const char* kAbilist32Prop = "ro.product.cpu.abilist32";
+ const char* kAbilist64Prop = "ro.product.cpu.abilist64";
+
+ // If the properties are defined explicitly, just use them.
+ if (base::GetProperty(kAbilistProp, EMPTY) != EMPTY) {
+ return;
+ }
+
+ std::string abilist32_prop_val;
+ std::string abilist64_prop_val;
+ const auto abilist32_prop = "ro.system.product.cpu.abilist32";
+ const auto abilist64_prop = "ro.system.product.cpu.abilist64";
+ abilist32_prop_val = base::GetProperty(abilist32_prop, EMPTY);
+ abilist64_prop_val = base::GetProperty(abilist64_prop, EMPTY);
+
+ // Merge ABI lists for ro.product.cpu.abilist
+ auto abilist_prop_val = abilist64_prop_val;
+ if (abilist32_prop_val != EMPTY) {
+ if (abilist_prop_val != EMPTY) {
+ abilist_prop_val += ",";
+ }
+ abilist_prop_val += abilist32_prop_val;
+ }
+
+ // Set these properties
+ const std::pair<const char*, const std::string&> set_prop_list[] = {
+ {kAbilistProp, abilist_prop_val},
+ {kAbilist32Prop, abilist32_prop_val},
+ {kAbilist64Prop, abilist64_prop_val},
+ };
+ for (const auto& [prop, prop_val] : set_prop_list) {
+ base::SetProperty(prop, prop_val);
+ }
+}
+
+static void* mmapFile(const char* dataFilePath) {
+#ifdef _WIN32
+ // Windows needs file path in wide chars to handle unicode file paths
+ int size = MultiByteToWideChar(CP_UTF8, 0, dataFilePath, -1, NULL, 0);
+ std::vector<wchar_t> wideDataFilePath(size);
+ MultiByteToWideChar(CP_UTF8, 0, dataFilePath, -1, wideDataFilePath.data(), size);
+ HANDLE file =
+ CreateFileW(wideDataFilePath.data(), GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr);
+ if ((HANDLE)INVALID_HANDLE_VALUE == file) {
+ return nullptr;
+ }
+
+ struct CloseHandleWrapper {
+ void operator()(HANDLE h) { CloseHandle(h); }
+ };
+ std::unique_ptr<void, CloseHandleWrapper> mmapHandle(
+ CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr));
+ if (!mmapHandle) {
+ return nullptr;
+ }
+ return MapViewOfFile(mmapHandle.get(), FILE_MAP_READ, 0, 0, 0);
+#else
+ int fd = open(dataFilePath, O_RDONLY);
+ if (fd == -1) {
+ return nullptr;
+ }
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ close(fd);
+ return nullptr;
+ }
+
+ void* addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (addr == MAP_FAILED) {
+ close(fd);
+ return nullptr;
+ }
+
+ close(fd);
+ return addr;
+#endif
+}
+
+static bool init_icu(const char* dataPath) {
+ void* addr = mmapFile(dataPath);
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(addr, &err);
+ if (err != U_ZERO_ERROR) {
+ return false;
+ }
+ return true;
+}
+
} // namespace android
using namespace android;
@@ -175,37 +330,82 @@
return JNI_ERR;
}
+ init_android_graphics();
+
// Configuration is stored as java System properties.
// Get a reference to System.getProperty
jclass system = FindClassOrDie(env, "java/lang/System");
jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
- // Get the names of classes that have to delegate their native methods
- auto delegateNativesToNativesString =
- (jstring) env->CallStaticObjectMethod(system,
- getPropertyMethod, env->NewStringUTF("delegate_natives_to_natives"),
- env->NewStringUTF(""));
- classesToDelegate = parseCsv(env, delegateNativesToNativesString);
-
// Get the names of classes that need to register their native methods
auto nativesClassesJString =
- (jstring) env->CallStaticObjectMethod(system,
- getPropertyMethod, env->NewStringUTF("native_classes"),
- env->NewStringUTF(""));
+ (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+ env->NewStringUTF("core_native_classes"),
+ env->NewStringUTF(""));
vector<string> classesToRegister = parseCsv(env, nativesClassesJString);
+ jstring registerProperty =
+ (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+ env->NewStringUTF(
+ "register_properties_during_load"),
+ env->NewStringUTF(""));
+ const char* registerPropertyString = env->GetStringUTFChars(registerProperty, 0);
+ if (strcmp(registerPropertyString, "true") == 0) {
+ // Set the system properties first as they could be used in the static initialization of
+ // other classes
+ if (register_android_os_SystemProperties(env) < 0) {
+ return JNI_ERR;
+ }
+ classesToRegister.erase(find(classesToRegister.begin(), classesToRegister.end(),
+ "android.os.SystemProperties"));
+ bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
+ bridge = MakeGlobalRefOrDie(env, bridge);
+ jmethodID setSystemPropertiesMethod =
+ GetStaticMethodIDOrDie(env, bridge, "setSystemProperties", "()V");
+ env->CallStaticVoidMethod(bridge, setSystemPropertiesMethod);
+ property_initialize_ro_cpu_abilist();
+ }
+ env->ReleaseStringUTFChars(registerProperty, registerPropertyString);
+
if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
return JNI_ERR;
}
// Set the location of ICU data
- auto stringPath = (jstring) env->CallStaticObjectMethod(system,
- getPropertyMethod, env->NewStringUTF("icu.dir"),
- env->NewStringUTF(""));
+ auto stringPath = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+ env->NewStringUTF("icu.data.path"),
+ env->NewStringUTF(""));
const char* path = env->GetStringUTFChars(stringPath, 0);
- u_setDataDirectory(path);
+ bool icuInitialized = init_icu(path);
env->ReleaseStringUTFChars(stringPath, path);
+ if (!icuInitialized) {
+ return JNI_ERR;
+ }
+
+ jstring useJniProperty =
+ (jstring)env->CallStaticObjectMethod(system, getPropertyMethod,
+ env->NewStringUTF("use_bridge_for_logging"),
+ env->NewStringUTF(""));
+ const char* useJniString = env->GetStringUTFChars(useJniProperty, 0);
+ if (strcmp(useJniString, "true") == 0) {
+ layoutLog = FindClassOrDie(env, "com/android/ide/common/rendering/api/ILayoutLog");
+ layoutLog = MakeGlobalRefOrDie(env, layoutLog);
+ logMethodId = GetMethodIDOrDie(env, layoutLog, "logAndroidFramework",
+ "(ILjava/lang/String;Ljava/lang/String;)V");
+ if (bridge == nullptr) {
+ bridge = FindClassOrDie(env, "com/android/layoutlib/bridge/Bridge");
+ bridge = MakeGlobalRefOrDie(env, bridge);
+ }
+ getLogId = GetStaticMethodIDOrDie(env, bridge, "getLog",
+ "()Lcom/android/ide/common/rendering/api/ILayoutLog;");
+ android::base::SetLogger(LayoutlibLogger);
+ android::base::SetAborter(LayoutlibAborter);
+ } else {
+ // initialize logging, so ANDROD_LOG_TAGS env variable is respected
+ android::base::InitLogging(nullptr, android::base::StderrLogger);
+ }
+ env->ReleaseStringUTFChars(useJniProperty, useJniString);
// Use English locale for number format to ensure correct parsing of floats when using strtof
setlocale(LC_NUMERIC, "en_US.UTF-8");
@@ -213,3 +413,9 @@
return JNI_VERSION_1_6;
}
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void*) {
+ JNIEnv* env = nullptr;
+ vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+ env->DeleteGlobalRef(bridge);
+ env->DeleteGlobalRef(layoutLog);
+}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3a2401a..7150fca 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3364,7 +3364,7 @@
area for the user and that ideally it should not be covered. Setting this is only
appropriate for UI where the user would likely take action to uncover it.
<p>The system will try to respect this, but when not possible will ignore it.
- See {@link android.view.View#setPreferKeepClear}. -->
+ <p>This is equivalent to {@link android.view.View#setPreferKeepClear}.-->
<attr name="preferKeepClear" format="boolean" />
<!-- <p>Whether or not the auto handwriting initiation is enabled in this View.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4874e65..39b41b5 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -991,4 +991,7 @@
<dimen name="secondary_rounded_corner_radius_adjustment">0px</dimen>
<dimen name="secondary_rounded_corner_radius_top_adjustment">0px</dimen>
<dimen name="secondary_rounded_corner_radius_bottom_adjustment">0px</dimen>
+
+ <!-- Default size for user icons (a.k.a. avatar images) -->
+ <dimen name="user_icon_size">190dp</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e27aa83..f531c3a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -545,6 +545,7 @@
<java-symbol type="dimen" name="immersive_mode_cling_width" />
<java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
<java-symbol type="dimen" name="circular_display_mask_thickness" />
+ <java-symbol type="dimen" name="user_icon_size" />
<java-symbol type="string" name="add_account_button_label" />
<java-symbol type="string" name="addToDictionary" />
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index a2bc77a..3a27225 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -29,6 +29,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
/**
* Unit test for {@link AndroidFuture}.
@@ -154,4 +155,35 @@
expectThrows(ExecutionException.class, future1::get);
assertThat(executionException.getCause()).isInstanceOf(UnsupportedOperationException.class);
}
+
+ @Test
+ public void testThenCombine() throws Exception {
+ String nearFutureString = "near future comes";
+ AndroidFuture<String> nearFuture = AndroidFuture.supply(() -> nearFutureString);
+ String farFutureString = " before far future.";
+ AndroidFuture<String> farFuture = AndroidFuture.supply(() -> farFutureString);
+ AndroidFuture<String> combinedFuture =
+ nearFuture.thenCombine(farFuture, ((s1, s2) -> s1 + s2));
+
+ assertThat(combinedFuture.get()).isEqualTo(nearFutureString + farFutureString);
+ }
+
+ @Test
+ public void testThenCombine_functionThrowingException() throws Exception {
+ String nearFutureString = "near future comes";
+ AndroidFuture<String> nearFuture = AndroidFuture.supply(() -> nearFutureString);
+ String farFutureString = " before far future.";
+ AndroidFuture<String> farFuture = AndroidFuture.supply(() -> farFutureString);
+ UnsupportedOperationException exception = new UnsupportedOperationException(
+ "Unsupported operation exception thrown!");
+ BiFunction<String, String, String> throwingFunction = (s1, s2) -> {
+ throw exception;
+ };
+ AndroidFuture<String> combinedFuture = nearFuture.thenCombine(farFuture, throwingFunction);
+
+ ExecutionException thrown = expectThrows(ExecutionException.class,
+ () -> combinedFuture.get());
+
+ assertThat(thrown.getCause()).isSameInstanceAs(exception);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 6ffcf10..241f1a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -423,7 +423,6 @@
WindowContainerTransaction t) {
// This is triggered right before the rotation is applied
if (fromRotation != toRotation) {
- mBubblePositioner.setRotation(toRotation);
if (mStackView != null) {
// Layout listener set on stackView will update the positioner
// once the rotation is applied
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 127d5a8..75b19fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PointF;
@@ -112,10 +113,6 @@
update();
}
- public void setRotation(int rotation) {
- mRotation = rotation;
- }
-
/**
* Available space and inset information. Call this when config changes
* occur or when added to a window.
@@ -273,7 +270,8 @@
/** @return whether the device is in landscape orientation. */
public boolean isLandscape() {
- return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+ return mContext.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
}
/** @return whether the screen is considered large. */
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3887372..6e695e6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7294,8 +7294,13 @@
* Ultrasound playback and capture, false otherwise.
*/
@SystemApi
- public static boolean isUltrasoundSupported() {
- return AudioSystem.isUltrasoundSupported();
+ @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+ public boolean isUltrasoundSupported() {
+ try {
+ return getService().isUltrasoundSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 2c9f015..d702eb9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -138,6 +138,8 @@
boolean isMicrophoneMuted();
+ boolean isUltrasoundSupported();
+
void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag);
oneway void setMicrophoneMuteFromSwitch(boolean on);
diff --git a/native/android/input.cpp b/native/android/input.cpp
index c06c81e..a231d8f 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -283,6 +283,21 @@
axis, pointer_index, history_index);
}
+int32_t AMotionEvent_getActionButton(const AInputEvent* motion_event) {
+ return static_cast<const MotionEvent*>(motion_event)->getActionButton();
+}
+
+int32_t AMotionEvent_getClassification(const AInputEvent* motion_event) {
+ switch (static_cast<const MotionEvent*>(motion_event)->getClassification()) {
+ case android::MotionClassification::NONE:
+ return AMOTION_EVENT_CLASSIFICATION_NONE;
+ case android::MotionClassification::AMBIGUOUS_GESTURE:
+ return AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE;
+ case android::MotionClassification::DEEP_PRESS:
+ return AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS;
+ }
+}
+
const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) {
MotionEvent* eventSrc = android::android_view_MotionEvent_getNativePtr(env, motionEvent);
if (eventSrc == nullptr) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3009a36..67a98a9 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -114,8 +114,10 @@
ALooper_removeFd;
ALooper_wake;
AMotionEvent_getAction;
+ AMotionEvent_getActionButton; # introduced=Tiramisu
AMotionEvent_getAxisValue; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
AMotionEvent_getButtonState; # introduced-arm=14 introduced-arm64=21 introduced-mips=14 introduced-mips64=21 introduced-x86=14 introduced-x86_64=21
+ AMotionEvent_getClassification; # introduced=Tiramisu
AMotionEvent_getDownTime;
AMotionEvent_getEdgeFlags;
AMotionEvent_getEventTime;
diff --git a/packages/CompanionDeviceManager/Android.bp b/packages/CompanionDeviceManager/Android.bp
index 0e60873..6ded1637 100644
--- a/packages/CompanionDeviceManager/Android.bp
+++ b/packages/CompanionDeviceManager/Android.bp
@@ -39,6 +39,7 @@
static_libs: [
"androidx.lifecycle_lifecycle-livedata",
"androidx.lifecycle_lifecycle-extensions",
+ "androidx.recyclerview_recyclerview",
"androidx.appcompat_appcompat",
],
diff --git a/packages/CompanionDeviceManager/res/color/selector.xml b/packages/CompanionDeviceManager/res/color/selector.xml
new file mode 100644
index 0000000..fda827d
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/color/selector.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:color="@android:color/darker_gray"/> <!-- pressed -->
+ <item android:color="@android:color/white"/>
+</selector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index 313e164..70cbfdf 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -49,8 +49,8 @@
android:layout_height="0dp"
android:layout_weight="1">
- <ListView
- android:id="@+id/device_list"
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/device_list"
style="@android:style/Widget.Material.ListView"
android:layout_width="match_parent"
android:layout_height="200dp" />
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index d79aea6..153fc1f 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -19,7 +19,8 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
- android:padding="12dp">
+ android:padding="12dp"
+ android:background="@color/selector">
<!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 16e851b..b51d310 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -46,10 +46,11 @@
import android.util.Log;
import android.view.View;
import android.widget.Button;
-import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
@@ -94,9 +95,9 @@
// regular.
private Button mButtonAllow;
- // The list is only shown for multiple-device regular association request, after at least one
- // matching device is found.
- private @Nullable ListView mListView;
+ // The recycler view is only shown for multiple-device regular association request, after
+ // at least one matching device is found.
+ private @Nullable RecyclerView mRecyclerView;
private @Nullable DeviceListAdapter mAdapter;
// The flag used to prevent double taps, that may lead to sending several requests for creating
@@ -195,14 +196,15 @@
mTitle = findViewById(R.id.title);
mSummary = findViewById(R.id.summary);
- mListView = findViewById(R.id.device_list);
- mListView.setOnItemClickListener((av, iv, position, id) -> onListItemClick(position));
+ mRecyclerView = findViewById(R.id.device_list);
+ mAdapter = new DeviceListAdapter(this, this::onListItemClick);
mButtonAllow = findViewById(R.id.btn_positive);
mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
findViewById(R.id.btn_negative).setOnClickListener(this::onNegativeButtonClick);
final CharSequence appLabel = getApplicationLabel(this, mRequest.getPackageName());
+
if (mRequest.isSelfManaged()) {
initUiForSelfManagedAssociation(appLabel);
} else if (mRequest.isSingleDevice()) {
@@ -333,7 +335,7 @@
mTitle.setText(title);
mSummary.setText(summary);
- mListView.setVisibility(View.GONE);
+ mRecyclerView.setVisibility(View.GONE);
}
private void initUiForSingleDevice(CharSequence appLabel) {
@@ -345,12 +347,12 @@
deviceFilterPairs -> updateSingleDeviceUi(
deviceFilterPairs, deviceProfile, appLabel));
- mListView.setVisibility(View.GONE);
+ mRecyclerView.setVisibility(View.GONE);
}
private void updateSingleDeviceUi(List<DeviceFilterPair<?>> deviceFilterPairs,
String deviceProfile, CharSequence appLabel) {
- // Ignore "empty" scan repots.
+ // Ignore "empty" scan reports.
if (deviceFilterPairs.isEmpty()) return;
mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
@@ -393,10 +395,12 @@
mTitle.setText(title);
mSummary.setText(summary);
- mAdapter = new DeviceListAdapter(this);
+ mAdapter = new DeviceListAdapter(this, this::onListItemClick);
// TODO: hide the list and show a spinner until a first device matching device is found.
- mListView.setAdapter(mAdapter);
+ mRecyclerView.setAdapter(mAdapter);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
CompanionDeviceDiscoveryService.getScanResult().observe(
/* lifecycleOwner */ this,
/* observer */ mAdapter);
@@ -414,6 +418,8 @@
if (DEBUG) Log.w(TAG, "Already selected.");
return;
}
+ // Notify the adapter to highlight the selected item.
+ mAdapter.setSelectedPosition(position);
mSelectedDevice = requireNonNull(selectedDevice);
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
index 198b778..e5513b0 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceListAdapter.java
@@ -15,73 +15,84 @@
*/
package com.android.companiondevicemanager;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.lifecycle.Observer;
+import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
-
/**
* Adapter for the list of "found" devices.
*/
-class DeviceListAdapter extends BaseAdapter implements Observer<List<DeviceFilterPair<?>>> {
+class DeviceListAdapter extends RecyclerView.Adapter<DeviceListAdapter.ViewHolder> implements
+ Observer<List<DeviceFilterPair<?>>> {
+ public int mSelectedPosition = RecyclerView.NO_POSITION;
+
private final Context mContext;
// List if pairs (display name, address)
private List<DeviceFilterPair<?>> mDevices;
- DeviceListAdapter(Context context) {
+ private OnItemClickListener mListener;
+
+ private static final int TYPE_WIFI = 0;
+ private static final int TYPE_BT = 1;
+
+ DeviceListAdapter(Context context, OnItemClickListener listener) {
mContext = context;
+ mListener = listener;
}
- @Override
- public int getCount() {
- return mDevices != null ? mDevices.size() : 0;
- }
-
- @Override
public DeviceFilterPair<?> getItem(int position) {
return mDevices.get(position);
}
@Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(
+ R.layout.list_item_device, parent, false);
+ ViewHolder viewHolder = new ViewHolder(view);
+ if (viewType == TYPE_WIFI) {
+ viewHolder.mImageView.setImageDrawable(getIcon(
+ com.android.internal.R.drawable.ic_wifi_signal_3));
+ } else {
+ viewHolder.mImageView.setImageDrawable(getIcon(
+ android.R.drawable.stat_sys_data_bluetooth));
+ }
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ holder.itemView.setSelected(mSelectedPosition == position);
+ holder.mTextView.setText(mDevices.get(position).getDisplayName());
+ holder.itemView.setOnClickListener(v -> mListener.onItemClick(position));
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return isWifiDevice(position) ? TYPE_WIFI : TYPE_BT;
+ }
+
+ @Override
public long getItemId(int position) {
return position;
}
@Override
- public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
- final View view = convertView != null
- ? convertView
- : LayoutInflater.from(mContext).inflate(R.layout.list_item_device, parent, false);
-
- final DeviceFilterPair<?> item = getItem(position);
- bindView(view, item);
-
- return view;
+ public int getItemCount() {
+ return mDevices != null ? mDevices.size() : 0;
}
- private void bindView(@NonNull View view, DeviceFilterPair<?> item) {
- final TextView textView = view.findViewById(android.R.id.text1);
- textView.setText(item.getDisplayName());
-
- final ImageView iconView = view.findViewById(android.R.id.icon);
-
- // TODO(b/211417476): Set either Bluetooth or WiFi icon.
- iconView.setVisibility(View.GONE);
- // final int iconRes = isBt ? android.R.drawable.stat_sys_data_bluetooth
- // : com.android.internal.R.drawable.ic_wifi_signal_3;
- // final Drawable icon = getTintedIcon(mResources, iconRes);
- // iconView.setImageDrawable(icon);
+ public void setSelectedPosition(int position) {
+ mSelectedPosition = position;
}
@Override
@@ -89,4 +100,28 @@
mDevices = deviceFilterPairs;
notifyDataSetChanged();
}
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+ private TextView mTextView;
+ private ImageView mImageView;
+ ViewHolder(View itemView) {
+ super(itemView);
+ mTextView = itemView.findViewById(android.R.id.text1);
+ mImageView = itemView.findViewById(android.R.id.icon);
+ }
+ }
+
+ private boolean isWifiDevice(int position) {
+ return mDevices.get(position).getDevice() instanceof android.net.wifi.ScanResult;
+ }
+
+ private Drawable getIcon(int resId) {
+ Drawable icon = mContext.getResources().getDrawable(resId, null);
+ icon.setTint(Color.DKGRAY);
+ return icon;
+ }
+
+ public interface OnItemClickListener {
+ void onItemClick(int position);
+ }
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index 1a955c4..72243f9 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -146,7 +146,7 @@
* @param iface the name of the interface.
* @param state the current state of the interface, or {@link #STATE_ABSENT} if the
* interface was removed.
- * @param role whether the interface is in the client mode or server mode.
+ * @param role whether the interface is in client mode or server mode.
* @param configuration the current IP configuration of the interface.
* @hide
*/
diff --git a/packages/SettingsLib/ActivityEmbedding/Android.bp b/packages/SettingsLib/ActivityEmbedding/Android.bp
index fc82b79..2569198 100644
--- a/packages/SettingsLib/ActivityEmbedding/Android.bp
+++ b/packages/SettingsLib/ActivityEmbedding/Android.bp
@@ -14,6 +14,8 @@
static_libs: [
"androidx.annotation_annotation",
+ "androidx.core_core",
+ "windowExtLib",
"SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
index 7f17d26..44b3b4e 100644
--- a/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
+++ b/packages/SettingsLib/ActivityEmbedding/src/com/android/settingslib/activityembedding/ActivityEmbeddingUtils.java
@@ -16,8 +16,14 @@
package com.android.settingslib.activityembedding;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import androidx.core.os.BuildCompat;
+import androidx.window.embedding.SplitController;
import com.android.settingslib.utils.BuildCompatUtils;
@@ -44,6 +50,33 @@
return false;
}
+ /**
+ * Whether current activity is embedded in the Settings app or not.
+ */
+ public static boolean isActivityEmbedded(Activity activity) {
+ return SplitController.getInstance().isActivityEmbedded(activity);
+ }
+
+ /**
+ * Whether current activity is suggested to show back button or not.
+ */
+ public static boolean shouldHideBackButton(Activity activity, boolean isSecondaryLayerPage) {
+ if (!BuildCompat.isAtLeastT()) {
+ return false;
+ }
+ if (!isSecondaryLayerPage) {
+ return false;
+ }
+ final String shouldHideBackButton = Settings.Global.getString(activity.getContentResolver(),
+ "settings_hide_secondary_page_back_button_in_two_pane");
+
+ if (TextUtils.isEmpty(shouldHideBackButton)
+ || TextUtils.equals("true", shouldHideBackButton)) {
+ return isActivityEmbedded(activity);
+ }
+ return false;
+ }
+
private ActivityEmbeddingUtils() {
}
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 684f4de..7a5ea47 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -48,7 +48,6 @@
"SettingsLibCollapsingToolbarBaseActivity",
"SettingsLibTwoTargetPreference",
"SettingsLibSettingsTransition",
- "SettingsLibActivityEmbedding",
"SettingsLibButtonPreference",
"setupdesign",
],
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 25d6c555..d893d09 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -22,8 +22,6 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
- <dimen name="circle_avatar_size">190dp</dimen>
-
<!-- Height of a user icon view -->
<dimen name="user_icon_view_height">24dp</dimen>
<!-- User spinner -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 883e080..f6e3557 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -144,7 +144,7 @@
* Returns a circular icon for a user.
*/
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
- final int iconSize = UserIconDrawable.getSizeForList(context);
+ final int iconSize = UserIconDrawable.getDefaultSize(context);
if (user.isManagedProfile()) {
Drawable drawable = UserIconDrawable.getManagedUserDrawable(context);
drawable.setBounds(0, 0, iconSize, iconSize);
diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
index fc38ada..afd3626 100644
--- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java
@@ -22,7 +22,6 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.UserHandle;
@@ -34,10 +33,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
import java.util.Set;
/**
@@ -52,12 +48,11 @@
private static DeviceStateRotationLockSettingsManager sSingleton;
private final ContentResolver mContentResolver;
+ private final String[] mDeviceStateRotationLockDefaults;
private final Handler mMainHandler = Handler.getMain();
private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
- private String[] mDeviceStateRotationLockDefaults;
private SparseIntArray mDeviceStateRotationLockSettings;
private SparseIntArray mDeviceStateRotationLockFallbackSettings;
- private List<SettableDeviceState> mSettableDeviceStates;
private DeviceStateRotationLockSettingsManager(Context context) {
mContentResolver = context.getContentResolver();
@@ -78,12 +73,6 @@
return sSingleton;
}
- /** Resets the singleton instance of this class. Only used for testing. */
- @VisibleForTesting
- public static synchronized void resetInstance() {
- sSingleton = null;
- }
-
/** Returns true if device-state based rotation lock settings are enabled. */
public static boolean isDeviceStateRotationLockEnabled(Context context) {
return context.getResources()
@@ -191,12 +180,6 @@
return true;
}
- /** Returns a list of device states and their respective auto-rotation setting availability. */
- public List<SettableDeviceState> getSettableDeviceStates() {
- // Returning a copy to make sure that nothing outside can mutate our internal list.
- return new ArrayList<>(mSettableDeviceStates);
- }
-
private void initializeInMemoryMap() {
String serializedSetting =
Settings.Secure.getStringForUser(
@@ -232,17 +215,6 @@
}
}
- /**
- * Resets the state of the class and saved settings back to the default values provided by the
- * resources config.
- */
- @VisibleForTesting
- public void resetStateForTesting(Resources resources) {
- mDeviceStateRotationLockDefaults =
- resources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
- fallbackOnDefaults();
- }
-
private void fallbackOnDefaults() {
loadDefaults();
persistSettings();
@@ -279,7 +251,6 @@
}
private void loadDefaults() {
- mSettableDeviceStates = new ArrayList<>(mDeviceStateRotationLockDefaults.length);
mDeviceStateRotationLockSettings = new SparseIntArray(
mDeviceStateRotationLockDefaults.length);
mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
@@ -300,8 +271,6 @@
+ values.length);
}
}
- boolean isSettable = rotationLockSetting != DEVICE_STATE_ROTATION_LOCK_IGNORED;
- mSettableDeviceStates.add(new SettableDeviceState(deviceState, isSettable));
mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
} catch (NumberFormatException e) {
Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
@@ -331,38 +300,4 @@
/** Called whenever the settings have changed. */
void onSettingsChanged();
}
-
- /** Represents a device state and whether it has an auto-rotation setting. */
- public static class SettableDeviceState {
- private final int mDeviceState;
- private final boolean mIsSettable;
-
- SettableDeviceState(int deviceState, boolean isSettable) {
- mDeviceState = deviceState;
- mIsSettable = isSettable;
- }
-
- /** Returns the device state associated with this object. */
- public int getDeviceState() {
- return mDeviceState;
- }
-
- /** Returns whether there is an auto-rotation setting for this device state. */
- public boolean isSettable() {
- return mIsSettable;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof SettableDeviceState)) return false;
- SettableDeviceState that = (SettableDeviceState) o;
- return mDeviceState == that.mDeviceState && mIsSettable == that.mIsSettable;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceState, mIsSettable);
- }
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
index e5ea446..7db00f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -31,8 +31,6 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import com.android.settingslib.R;
-
/**
* Converts the user avatar icon to a circularly clipped one.
* TODO: Move this to an internal framework class and share with the one in Keyguard.
@@ -49,9 +47,9 @@
public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
Resources res = context.getResources();
- float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+ int iconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
- CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+ CircleFramedDrawable instance = new CircleFramedDrawable(icon, iconSize);
return instance;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 035fafd..d01f2b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -49,8 +49,6 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.os.BuildCompat;
-import com.android.settingslib.R;
-
/**
* Converts the user avatar icon to a circularly clipped one with an optional badge and frame
*/
@@ -120,8 +118,9 @@
* @param context
* @return size in pixels
*/
- public static int getSizeForList(Context context) {
- return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+ public static int getDefaultSize(Context context) {
+ return context.getResources()
+ .getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
}
public UserIconDrawable() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index f8bb38b..5862f60 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -16,19 +16,17 @@
package com.android.settingslib.users;
-import android.annotation.NonNull;
import android.app.Activity;
import android.content.Intent;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.Log;
import android.widget.ImageView;
import com.android.internal.util.UserIcons;
-import com.android.settingslib.R;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.settingslib.utils.ThreadUtils;
@@ -114,10 +112,10 @@
private void onDefaultIconSelected(int tintColor) {
try {
ThreadUtils.postOnBackgroundThread(() -> {
+ Resources res = mActivity.getResources();
Drawable drawable =
- UserIcons.getDefaultUserIconInColor(mActivity.getResources(), tintColor);
- Bitmap bitmap = convertToBitmap(drawable,
- (int) mActivity.getResources().getDimension(R.dimen.circle_avatar_size));
+ UserIcons.getDefaultUserIconInColor(res, tintColor);
+ Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, drawable);
ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
}).get();
@@ -126,14 +124,6 @@
}
}
- private static Bitmap convertToBitmap(@NonNull Drawable icon, int size) {
- Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- icon.setBounds(0, 0, size, size);
- icon.draw(canvas);
- return bitmap;
- }
-
private void onPhotoCropped(final Uri data) {
ThreadUtils.postOnBackgroundThread(() -> {
InputStream imageStream = null;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
deleted file mode 100644
index 8d687b6..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2022 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.settingslib.devicestate;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DeviceStateRotationLockSettingsManagerTest {
-
- @Mock private Context mMockContext;
- @Mock private Resources mMockResources;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- Context context = InstrumentationRegistry.getTargetContext();
- when(mMockContext.getApplicationContext()).thenReturn(mMockContext);
- when(mMockContext.getResources()).thenReturn(mMockResources);
- when(mMockContext.getContentResolver()).thenReturn(context.getContentResolver());
- }
-
- @Test
- public void getSettableDeviceStates_returnsExpectedValuesInOriginalOrder() {
- when(mMockResources.getStringArray(
- R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
- new String[]{"2:2", "4:0", "1:1", "0:0"});
-
- List<SettableDeviceState> settableDeviceStates =
- DeviceStateRotationLockSettingsManager.getInstance(
- mMockContext).getSettableDeviceStates();
-
- assertThat(settableDeviceStates).containsExactly(
- new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
- new SettableDeviceState(/* deviceState= */ 4, /* isSettable= */ false),
- new SettableDeviceState(/* deviceState= */ 1, /* isSettable= */ true),
- new SettableDeviceState(/* deviceState= */ 0, /* isSettable= */ false)
- ).inOrder();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index f982790..4a9a1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -263,22 +263,29 @@
inoutInfo.touchableRegion.set(getTouchRegion(true));
}
- private Region getTouchRegion(boolean includeScrim) {
- Region touchRegion = new Region();
+ private Region getSwipeRegion() {
+ Region swipeRegion = new Region();
final Rect tmpRect = new Rect();
mScreenshotPreview.getBoundsOnScreen(tmpRect);
tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
(int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
- touchRegion.op(tmpRect, Region.Op.UNION);
+ swipeRegion.op(tmpRect, Region.Op.UNION);
mActionsContainerBackground.getBoundsOnScreen(tmpRect);
tmpRect.inset((int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP),
(int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP));
- touchRegion.op(tmpRect, Region.Op.UNION);
+ swipeRegion.op(tmpRect, Region.Op.UNION);
mDismissButton.getBoundsOnScreen(tmpRect);
- touchRegion.op(tmpRect, Region.Op.UNION);
+ swipeRegion.op(tmpRect, Region.Op.UNION);
+
+ return swipeRegion;
+ }
+
+ private Region getTouchRegion(boolean includeScrim) {
+ Region touchRegion = getSwipeRegion();
if (includeScrim && mScrollingScrim.getVisibility() == View.VISIBLE) {
+ final Rect tmpRect = new Rect();
mScrollingScrim.getBoundsOnScreen(tmpRect);
touchRegion.op(tmpRect, Region.Op.UNION);
}
@@ -328,7 +335,7 @@
@Override // ViewGroup
public boolean onInterceptTouchEvent(MotionEvent ev) {
// scrolling scrim should not be swipeable; return early if we're on the scrim
- if (!getTouchRegion(false).contains((int) ev.getRawX(), (int) ev.getRawY())) {
+ if (!getSwipeRegion().contains((int) ev.getRawX(), (int) ev.getRawY())) {
return false;
}
// always pass through the down event so the swipe handler knows the initial state
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
index 3a270bb..0686071c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.java
@@ -19,6 +19,7 @@
import android.app.Dialog;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
@@ -70,10 +71,12 @@
}
Drawable newUserIcon = userIcon;
+ Resources res = mContext.getResources();
if (newUserIcon == null) {
- newUserIcon = UserIcons.getDefaultUserIcon(mContext.getResources(), user.id, false);
+ newUserIcon = UserIcons.getDefaultUserIcon(res, user.id, false);
}
- mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon));
+ mUserManager.setUserIcon(
+ user.id, UserIcons.convertToBitmapAtUserIconSize(res, newUserIcon));
userCreationProgressDialog.dismiss();
successCallback.accept(user);
diff --git a/services/Android.bp b/services/Android.bp
index e0ca8a6..2e4405f 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -54,7 +54,9 @@
SYSTEM_OPTIMIZE_JAVA: {
optimize: {
enabled: true,
- optimize: true,
+ // TODO(b/210510433): Enable optimizations after improving
+ // retracing infra.
+ optimize: false,
shrink: true,
proguard_flags_files: ["proguard.flags"],
},
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index be1bc79..c39b59a 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.os.Handler;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -120,6 +121,12 @@
mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors);
}
+ if (serviceConnectors.isEmpty()) {
+ Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: "
+ + packageName + ". Please check if they are correctly declared.");
+ return;
+ }
+
// The first connector in the list is always the primary connector: set a listener to it.
serviceConnectors.get(0).setListener(this::onPrimaryServiceBindingDied);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9c8ed5a..efbc4de 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -3019,14 +3019,32 @@
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
+
// Send the broadcast twice -- once for all apps with READ_PHONE_STATE, then again
- // for all apps with READ_PRIV but not READ_PHONE_STATE. This ensures that any app holding
- // either READ_PRIV or READ_PHONE get this broadcast exactly once.
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
- mContext.createContextAsUser(UserHandle.ALL, 0)
- .sendBroadcastMultiplePermissions(intent,
- new String[] { Manifest.permission.READ_PRIVILEGED_PHONE_STATE },
- new String[] { Manifest.permission.READ_PHONE_STATE });
+ // for all apps with READ_PRIVILEGED_PHONE_STATE but not READ_PHONE_STATE.
+ // Do this again twice, the first time for apps with ACCESS_FINE_LOCATION, then again with
+ // the location-sanitized service state for all apps without ACCESS_FINE_LOCATION.
+ // This ensures that any app holding either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE
+ // get this broadcast exactly once, and we are not exposing location without permission.
+ mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+ new String[] {Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION});
+ mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+ new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION},
+ new String[] {Manifest.permission.READ_PHONE_STATE});
+
+ // Replace bundle with location-sanitized ServiceState
+ data = new Bundle();
+ state.createLocationInfoSanitizedCopy(true).fillInNotifierBundle(data);
+ intent.putExtras(data);
+ mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+ new String[] {Manifest.permission.READ_PHONE_STATE},
+ new String[] {Manifest.permission.ACCESS_FINE_LOCATION});
+ mContext.createContextAsUser(UserHandle.ALL, 0).sendBroadcastMultiplePermissions(intent,
+ new String[] {Manifest.permission.READ_PRIVILEGED_PHONE_STATE},
+ new String[] {Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION});
}
private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6c1a00d..97dd323 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -1018,6 +1018,7 @@
Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
0,
mService.mUserController.getCurrentUserId()) != 0;
+ final String packageName = proc.info.packageName;
final boolean crashSilenced = mAppsNotReportingCrashes != null
&& mAppsNotReportingCrashes.contains(proc.info.packageName);
final long now = SystemClock.uptimeMillis();
@@ -1026,6 +1027,7 @@
if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
&& !crashSilenced && !shouldThottle
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
+ Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
errState.getDialogController().showCrashDialogs(data);
if (!proc.isolated) {
mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index 7f48d52..69f70ca 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -64,6 +64,8 @@
@GuardedBy("mLock")
private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>();
+ private volatile boolean mLockedBootCompleted = false;
+
AppPermissionTracker(Context context, AppRestrictionController controller) {
this(context, controller, null, null);
}
@@ -85,20 +87,20 @@
final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
for (int userId : allUsers) {
final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
if (apps == null) {
continue;
}
- synchronized (mLock) {
- final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor;
- final long now = SystemClock.elapsedRealtime();
- for (int i = 0, size = apps.size(); i < size; i++) {
- final ApplicationInfo ai = apps.get(i);
- for (String permission : permissions) {
- if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
- continue;
- }
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ final ApplicationInfo ai = apps.get(i);
+ for (String permission : permissions) {
+ if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
+ continue;
+ }
+ synchronized (mLock) {
ArraySet<String> grantedPermissions = uidPerms.get(ai.uid);
if (grantedPermissions == null) {
grantedPermissions = new ArraySet<String>();
@@ -132,25 +134,30 @@
private void handlePermissionsChanged(int uid) {
final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
if (permissions != null && permissions.length > 0) {
+ final PermissionManagerServiceInternal pm =
+ mInjector.getPermissionManagerServiceInternal();
+ final boolean[] states = new boolean[permissions.length];
+ for (int i = 0; i < permissions.length; i++) {
+ states[i] = pm.checkUidPermission(uid, permissions[i]) == PERMISSION_GRANTED;
+ if (DEBUG_PERMISSION_TRACKER) {
+ Slog.i(TAG, UserHandle.formatUid(uid) + " " + permissions[i] + "=" + states[i]);
+ }
+ }
synchronized (mLock) {
- handlePermissionsChangedLocked(uid);
+ handlePermissionsChangedLocked(uid, permissions, states);
}
}
}
@GuardedBy("mLock")
- private void handlePermissionsChangedLocked(int uid) {
- final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+ private void handlePermissionsChangedLocked(int uid, String[] permissions, boolean[] states) {
final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid);
ArraySet<String> grantedPermissions = index >= 0
? mUidGrantedPermissionsInMonitor.valueAt(index) : null;
- final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
final long now = SystemClock.elapsedRealtime();
- for (String permission: permissions) {
- boolean granted = pm.checkUidPermission(uid, permission) == PERMISSION_GRANTED;
- if (DEBUG_PERMISSION_TRACKER) {
- Slog.i(TAG, UserHandle.formatUid(uid) + " " + permission + "=" + granted);
- }
+ for (int i = 0; i < permissions.length; i++) {
+ final String permission = permissions[i];
+ final boolean granted = states[i];
boolean changed = false;
if (granted) {
if (grantedPermissions == null) {
@@ -200,6 +207,10 @@
}
private void onPermissionTrackerEnabled(boolean enabled) {
+ if (!mLockedBootCompleted) {
+ // Not ready, bail out.
+ return;
+ }
final PermissionManager pm = mInjector.getPermissionManager();
if (enabled) {
pm.addOnPermissionsChangeListener(this);
@@ -211,6 +222,12 @@
}
@Override
+ void onLockedBootCompleted() {
+ mLockedBootCompleted = true;
+ onPermissionTrackerEnabled(mInjector.getPolicy().isEnabled());
+ }
+
+ @Override
void dump(PrintWriter pw, String prefix) {
pw.print(prefix);
pw.println("APP PERMISSIONS TRACKER:");
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 1129c19..2ffd487 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -71,6 +71,7 @@
import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
import static android.os.PowerExemptionManager.reasonCodeToString;
import static android.os.Process.SYSTEM_UID;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -792,7 +793,7 @@
mInjector = injector;
mContext = injector.getContext();
mActivityManagerService = service;
- mBgHandlerThread = new HandlerThread("bgres-controller");
+ mBgHandlerThread = new HandlerThread("bgres-controller", THREAD_PRIORITY_BACKGROUND);
mBgHandlerThread.start();
mBgHandler = new BgHandler(mBgHandlerThread.getLooper(), injector);
mBgExecutor = new HandlerExecutor(mBgHandler);
@@ -816,9 +817,11 @@
mInjector.getAppStandbyInternal().addListener(mAppIdleStateChangeListener);
mInjector.getRoleManager().addOnRoleHoldersChangedListenerAsUser(mBgExecutor,
mRoleHolderChangedListener, UserHandle.ALL);
- for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
- mAppStateTrackers.get(i).onSystemReady();
- }
+ mInjector.scheduleInitTrackers(mBgHandler, () -> {
+ for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
+ mAppStateTrackers.get(i).onSystemReady();
+ }
+ });
}
@VisibleForTesting
@@ -2137,6 +2140,10 @@
}
return null;
}
+
+ void scheduleInitTrackers(Handler handler, Runnable initializers) {
+ handler.post(initializers);
+ }
}
private void registerForSystemBroadcasts() {
@@ -2221,6 +2228,21 @@
userFilter.addAction(Intent.ACTION_USER_REMOVED);
userFilter.addAction(Intent.ACTION_UID_REMOVED);
mContext.registerReceiverForAllUsers(broadcastReceiver, userFilter, null, mBgHandler);
+ final BroadcastReceiver bootReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ switch (intent.getAction()) {
+ case Intent.ACTION_LOCKED_BOOT_COMPLETED: {
+ onLockedBootCompleted();
+ } break;
+ }
+ }
+ };
+ final IntentFilter bootFilter = new IntentFilter();
+ bootFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
+ mContext.registerReceiverAsUser(bootReceiver, UserHandle.SYSTEM,
+ bootFilter, null, mBgHandler);
}
void forEachTracker(Consumer<BaseAppStateTracker> sink) {
@@ -2275,6 +2297,12 @@
mRestrictionSettings.removeUid(uid);
}
+ private void onLockedBootCompleted() {
+ for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
+ mAppStateTrackers.get(i).onLockedBootCompleted();
+ }
+ }
+
boolean isBgAutoRestrictedBucketFeatureFlagEnabled() {
return mConstantsObserver.mBgAutoRestrictedBucket;
}
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 482d697..0fada53 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -204,6 +204,12 @@
}
/**
+ * Called when the system sends LOCKED_BOOT_COMPLETED.
+ */
+ void onLockedBootCompleted() {
+ }
+
+ /**
* Called when a device config property in the activity manager namespace
* has changed.
*/
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9929ed7..86ca699 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1115,6 +1115,9 @@
int lastOomAdj = msg.arg1;
int procState = msg.arg2;
synchronized (mProcLock) {
+ if(mPendingCompactionProcesses.isEmpty()) {
+ return;
+ }
proc = mPendingCompactionProcesses.remove(0);
opt = proc.mOptRecord;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 807293f..0b9fb1a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3330,6 +3330,13 @@
}
}
+ private void enforceAccessUltrasoundPermission() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing ACCESS_ULTRASOUND permission");
+ }
+ }
+
private void enforceQueryStatePermission() {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3462,6 +3469,12 @@
attributionTag, Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
}
+ /** @see AudioManager#isUltrasoundSupported() */
+ public boolean isUltrasoundSupported() {
+ enforceAccessUltrasoundPermission();
+ return AudioSystem.isUltrasoundSupported();
+ }
+
private boolean canChangeAccessibilityVolume() {
synchronized (mAccessibilityServiceUidsLock) {
if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2c2a2bf..17215e5 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -131,6 +131,7 @@
private static final int MSG_STOP_SENSOR_LISTENER = 2;
private static final int MSG_START_SENSOR_LISTENER = 3;
private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4;
+ private static final int MSG_SENSOR_CHANGED = 5;
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
@@ -158,6 +159,7 @@
// These members should only be accessed on the mBgHandler thread.
private BroadcastReceiver mBroadcastReceiver;
private SensorListener mSensorListener;
+ private Sensor mLightSensor;
private SettingsObserver mSettingsObserver;
private DisplayListener mDisplayListener;
private boolean mSensorRegistered;
@@ -327,6 +329,14 @@
m.sendToTarget();
}
+ /**
+ * Updates the light sensor to use.
+ */
+ public void setLightSensor(Sensor lightSensor) {
+ mBgHandler.obtainMessage(MSG_SENSOR_CHANGED, 0 /*unused*/, 0/*unused*/, lightSensor)
+ .sendToTarget();
+ }
+
private void handleBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) {
@@ -428,13 +438,28 @@
}
}
+ private void handleSensorChanged(Sensor lightSensor) {
+ if (mLightSensor != lightSensor) {
+ mLightSensor = lightSensor;
+ stopSensorListener();
+ synchronized (mDataCollectionLock) {
+ mLastSensorReadings.clear();
+ }
+ // Attempt to restart the sensor listener. It will check to see if it should be running
+ // so there is no need to also check here.
+ startSensorListener();
+ }
+ }
+
private void startSensorListener() {
if (!mSensorRegistered
+ && mLightSensor != null
+ && mAmbientBrightnessStatsTracker != null
&& mInjector.isInteractive(mContext)
&& mInjector.isBrightnessModeAutomatic(mContentResolver)) {
mAmbientBrightnessStatsTracker.start();
mSensorRegistered = true;
- mInjector.registerSensorListener(mContext, mSensorListener,
+ mInjector.registerSensorListener(mContext, mSensorListener, mLightSensor,
mInjector.getBackgroundHandler());
}
}
@@ -736,6 +761,7 @@
pw.println("BrightnessTracker state:");
synchronized (mDataCollectionLock) {
pw.println(" mStarted=" + mStarted);
+ pw.println(" mLightSensor=" + mLightSensor);
pw.println(" mLastBatteryLevel=" + mLastBatteryLevel);
pw.println(" mLastBrightness=" + mLastBrightness);
pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size());
@@ -1017,6 +1043,9 @@
disableColorSampling();
}
break;
+ case MSG_SENSOR_CHANGED:
+ handleSensorChanged((Sensor) msg.obj);
+ break;
}
}
@@ -1045,9 +1074,8 @@
@VisibleForTesting
static class Injector {
public void registerSensorListener(Context context,
- SensorEventListener sensorListener, Handler handler) {
+ SensorEventListener sensorListener, Sensor lightSensor, Handler handler) {
SensorManager sensorManager = context.getSystemService(SensorManager.class);
- Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
sensorManager.registerListener(sensorListener,
lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 418e91d..9067f2e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -826,7 +826,6 @@
private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
// All properties that depend on the associated DisplayDevice and the DDC must be
// updated here.
- loadAmbientLightSensor();
loadBrightnessRampRates();
loadProximitySensor();
loadNitsRange(mContext.getResources());
@@ -972,6 +971,9 @@
}
loadAmbientLightSensor();
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.setLightSensor(mLightSensor);
+ }
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.stop();
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 39ee0f4..9b10058 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -40,6 +40,7 @@
import android.os.ServiceManager;
import android.os.Trace;
import android.sysprop.ApexProperties;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.PrintWriterPrinter;
@@ -608,18 +609,21 @@
continue;
}
- String name = service.getName();
- for (ApexSystemServiceInfo info : mApexSystemServices) {
- if (info.getName().equals(name)) {
- throw new IllegalStateException(String.format(
- "Duplicate apex-system-service %s from %s, %s",
- name, info.mJarPath, service.getJarPath()));
+ if (ai.isActive) {
+ String name = service.getName();
+ for (int j = 0; j < mApexSystemServices.size(); j++) {
+ ApexSystemServiceInfo info = mApexSystemServices.get(j);
+ if (info.getName().equals(name)) {
+ throw new IllegalStateException(TextUtils.formatSimple(
+ "Duplicate apex-system-service %s from %s, %s", name,
+ info.mJarPath, service.getJarPath()));
+ }
}
+ ApexSystemServiceInfo info = new ApexSystemServiceInfo(
+ service.getName(), service.getJarPath(),
+ service.getInitOrder());
+ mApexSystemServices.add(info);
}
-
- ApexSystemServiceInfo info = new ApexSystemServiceInfo(
- service.getName(), service.getJarPath(), service.getInitOrder());
- mApexSystemServices.add(info);
}
Collections.sort(mApexSystemServices);
mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8a105dd..70a030d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2684,8 +2684,8 @@
@GuardedBy("mLock")
private void updateUserActivitySummaryLocked(long now, int dirty) {
// Update the status of the user activity timeout timer.
- if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS
- | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) == 0) {
+ if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY
+ | DIRTY_WAKEFULNESS | DIRTY_SETTINGS | DIRTY_ATTENTIVE)) == 0) {
return;
}
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
@@ -2772,6 +2772,11 @@
screenDimDuration);
}
+ if (isAttentiveTimeoutExpired(powerGroup, now)) {
+ groupUserActivitySummary = 0;
+ groupNextTimeout = -1;
+ }
+
hasUserActivitySummary |= groupUserActivitySummary != 0;
if (nextTimeout == -1) {
@@ -3127,7 +3132,7 @@
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
msg.arg1 = powerGroup.getGroupId();
msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 33b807b..9893f68 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -106,12 +106,6 @@
synchronized (task.mWmService.mGlobalLock) {
activityRecord = task.topRunningActivity();
- if(!activityRecord.info.applicationInfo.isOnBackInvokedCallbackEnabled()) {
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Activity %s: enableOnBackInvokedCallback=false."
- + " Returning null BackNavigationInfo.", activityRecord.getName());
- return null;
- }
-
removedWindowContainer = activityRecord;
taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
WindowState window = task.getWindow(WindowState::isFocused);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 936940f..e6bb0ce 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -2648,6 +2648,11 @@
AppPermissionTracker getAppPermissionTracker() {
return mAppPermissionTracker;
}
+
+ @Override
+ void scheduleInitTrackers(Handler handler, Runnable initializers) {
+ initializers.run();
+ }
}
private class TestBaseTrackerInjector<T extends BaseAppStatePolicy>
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index bdf94f3..356600d 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -33,6 +33,7 @@
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
+import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.display.AmbientBrightnessDayStats;
@@ -42,6 +43,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.input.InputSensorInfo;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -63,6 +65,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -84,8 +88,11 @@
private static final String DEFAULT_DISPLAY_ID = "123";
private static final float FLOAT_DELTA = 0.01f;
+ @Mock private InputSensorInfo mInputSensorInfoMock;
+
private BrightnessTracker mTracker;
private TestInjector mInjector;
+ private Sensor mLightSensorFake;
private static Object sHandlerLock = new Object();
private static Handler sHandler;
@@ -108,9 +115,12 @@
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mInjector = new TestInjector(ensureHandler());
+ mLightSensorFake = new Sensor(mInputSensorInfoMock);
mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector);
+ mTracker.setLightSensor(mLightSensorFake);
mDefaultNightModeColorTemperature =
InstrumentationRegistry.getContext().getResources().getInteger(
R.integer.config_nightDisplayColorTemperatureDefault);
@@ -834,6 +844,47 @@
mTracker.stop();
}
+ @Test
+ public void testLightSensorChange() {
+ // verify the tracker started correctly and a listener registered
+ startTracker(mTracker);
+ assertNotNull(mInjector.mSensorListener);
+ assertEquals(mInjector.mLightSensor, mLightSensorFake);
+
+ // Setting the sensor to null should stop the registered listener.
+ mTracker.setLightSensor(null);
+ mInjector.waitForHandler();
+ assertNull(mInjector.mSensorListener);
+ assertNull(mInjector.mLightSensor);
+
+ // Resetting sensor should start listener again
+ mTracker.setLightSensor(mLightSensorFake);
+ mInjector.waitForHandler();
+ assertNotNull(mInjector.mSensorListener);
+ assertEquals(mInjector.mLightSensor, mLightSensorFake);
+
+ Sensor secondSensor = new Sensor(mInputSensorInfoMock);
+ // Setting a different listener should keep things working
+ mTracker.setLightSensor(secondSensor);
+ mInjector.waitForHandler();
+ assertNotNull(mInjector.mSensorListener);
+ assertEquals(mInjector.mLightSensor, secondSensor);
+ }
+
+ @Test
+ public void testSetLightSensorDoesntStartListener() {
+ mTracker.setLightSensor(mLightSensorFake);
+ assertNull(mInjector.mSensorListener);
+ }
+
+ @Test
+ public void testNullLightSensorWontRegister() {
+ mTracker.setLightSensor(null);
+ startTracker(mTracker);
+ assertNull(mInjector.mSensorListener);
+ assertNull(mInjector.mLightSensor);
+ }
+
private InputStream getInputStream(String data) {
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
}
@@ -924,6 +975,7 @@
private class TestInjector extends BrightnessTracker.Injector {
SensorEventListener mSensorListener;
+ Sensor mLightSensor;
BroadcastReceiver mBroadcastReceiver;
DisplayManager.DisplayListener mDisplayListener;
Map<String, Integer> mSecureIntSettings = new HashMap<>();
@@ -974,14 +1026,16 @@
@Override
public void registerSensorListener(Context context,
- SensorEventListener sensorListener, Handler handler) {
+ SensorEventListener sensorListener, Sensor lightSensor, Handler handler) {
mSensorListener = sensorListener;
+ mLightSensor = lightSensor;
}
@Override
public void unregisterSensorListener(Context context,
SensorEventListener sensorListener) {
mSensorListener = null;
+ mLightSensor = null;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 2f5993d1..5d3da43 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -132,7 +132,12 @@
@Test
public void testGetApexSystemServices() throws RemoteException {
- when(mApexService.getAllPackages()).thenReturn(createApexInfoForTestPkg(true, false));
+ when(mApexService.getAllPackages()).thenReturn(new ApexInfo[] {
+ createApexInfoForTestPkg(false, true, 1),
+ // only active apex reports apex-system-service
+ createApexInfoForTestPkg(true, false, 2),
+ });
+
mApexManager.scanApexPackagesTraced(mPackageParser2,
ParallelPackageParser.makeExecutorService());
@@ -484,17 +489,20 @@
assertThat(e).hasMessageThat().contains("Failed to collect certificates from ");
}
- private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) {
+ private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) {
File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME);
ApexInfo apexInfo = new ApexInfo();
apexInfo.isActive = isActive;
apexInfo.isFactory = isFactory;
apexInfo.moduleName = TEST_APEX_PKG;
apexInfo.modulePath = apexFile.getPath();
- apexInfo.versionCode = 191000070;
+ apexInfo.versionCode = version;
apexInfo.preinstalledModulePath = apexFile.getPath();
+ return apexInfo;
+ }
- return new ApexInfo[]{apexInfo};
+ private ApexInfo[] createApexInfoForTestPkg(boolean isActive, boolean isFactory) {
+ return new ApexInfo[]{createApexInfoForTestPkg(isActive, isFactory, 191000070)};
}
private ApexInfo createApexInfo(String moduleName, int versionCode, boolean isActive,
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index c94168c..ed7aac7 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1058,6 +1058,23 @@
assertThat(mService.getGlobalWakefulnessLocked()).isNotEqualTo(WAKEFULNESS_ASLEEP);
}
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testInattentiveSleep_goesToSleepFromDream() throws Exception {
+ setAttentiveTimeout(20000);
+ createService();
+ startSystem();
+ setPluggedIn(true);
+ forceAwake();
+ forceDream();
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+
+ advanceTime(20500);
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP);
+ }
+
@Test
public void testWakeLock_affectsProperDisplayGroup() {
final int nonDefaultDisplayGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index 27e8d69..ab8f69b 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -22,6 +22,7 @@
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -39,6 +40,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
class BroadcastResponseStatsTracker {
private static final String TAG = "ResponseStatsTracker";
@@ -172,28 +175,27 @@
}
}
- @NonNull BroadcastResponseStats queryBroadcastResponseStats(int callingUid,
- @NonNull String packageName, long id, @UserIdInt int userId) {
- final BroadcastResponseStats aggregatedResponseStats =
- new BroadcastResponseStats(packageName);
+ @NonNull List<BroadcastResponseStats> queryBroadcastResponseStats(int callingUid,
+ @Nullable String packageName, @IntRange(from = 0) long id, @UserIdInt int userId) {
+ final List<BroadcastResponseStats> broadcastResponseStatsList = new ArrayList<>();
synchronized (mLock) {
final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
mUserResponseStats.get(callingUid);
if (responseStatsForCaller == null) {
- return aggregatedResponseStats;
+ return broadcastResponseStatsList;
}
final UserBroadcastResponseStats responseStatsForUser =
responseStatsForCaller.get(userId);
if (responseStatsForUser == null) {
- return aggregatedResponseStats;
+ return broadcastResponseStatsList;
}
- responseStatsForUser.aggregateBroadcastResponseStats(aggregatedResponseStats,
- packageName, id);
+ responseStatsForUser.populateAllBroadcastResponseStats(
+ broadcastResponseStatsList, packageName, id);
}
- return aggregatedResponseStats;
+ return broadcastResponseStatsList;
}
- void clearBroadcastResponseStats(int callingUid, @NonNull String packageName, long id,
+ void clearBroadcastResponseStats(int callingUid, @Nullable String packageName, long id,
@UserIdInt int userId) {
synchronized (mLock) {
final SparseArray<UserBroadcastResponseStats> responseStatsForCaller =
@@ -210,6 +212,16 @@
}
}
+ void clearBroadcastEvents(int callingUid, @UserIdInt int userId) {
+ synchronized (mLock) {
+ final UserBroadcastEvents userBroadcastEvents = mUserBroadcastEvents.get(userId);
+ if (userBroadcastEvents == null) {
+ return;
+ }
+ userBroadcastEvents.clear(callingUid);
+ }
+ }
+
void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) {
mUserBroadcastEvents.remove(userId);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4a761a7..06aa8f0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -49,7 +49,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.AppLaunchEstimateInfo;
import android.app.usage.AppStandbyInfo;
-import android.app.usage.BroadcastResponseStats;
+import android.app.usage.BroadcastResponseStatsList;
import android.app.usage.ConfigurationStats;
import android.app.usage.EventStats;
import android.app.usage.IUsageStatsManager;
@@ -2686,16 +2686,15 @@
@Override
@NonNull
- public BroadcastResponseStats queryBroadcastResponseStats(
- @NonNull String packageName,
- @IntRange(from = 1) long id,
+ public BroadcastResponseStatsList queryBroadcastResponseStats(
+ @Nullable String packageName,
+ @IntRange(from = 0) long id,
@NonNull String callingPackage,
@UserIdInt int userId) {
- Objects.requireNonNull(packageName);
Objects.requireNonNull(callingPackage);
// TODO: Move to Preconditions utility class
- if (id <= 0) {
- throw new IllegalArgumentException("id needs to be >0");
+ if (id < 0) {
+ throw new IllegalArgumentException("id needs to be >=0");
}
final int callingUid = Binder.getCallingUid();
@@ -2708,8 +2707,9 @@
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
userId, false /* allowAll */, false /* requireFull */,
"queryBroadcastResponseStats" /* name */, callingPackage);
- return mResponseStatsTracker.queryBroadcastResponseStats(
- callingUid, packageName, id, userId);
+ return new BroadcastResponseStatsList(
+ mResponseStatsTracker.queryBroadcastResponseStats(
+ callingUid, packageName, id, userId));
}
@Override
@@ -2718,10 +2718,9 @@
@IntRange(from = 1) long id,
@NonNull String callingPackage,
@UserIdInt int userId) {
- Objects.requireNonNull(packageName);
Objects.requireNonNull(callingPackage);
- if (id <= 0) {
- throw new IllegalArgumentException("id needs to be >0");
+ if (id < 0) {
+ throw new IllegalArgumentException("id needs to be >=0");
}
final int callingUid = Binder.getCallingUid();
@@ -2737,6 +2736,23 @@
mResponseStatsTracker.clearBroadcastResponseStats(callingUid,
packageName, id, userId);
}
+
+ @Override
+ public void clearBroadcastEvents(@NonNull String callingPackage, @UserIdInt int userId) {
+ Objects.requireNonNull(callingPackage);
+
+ final int callingUid = Binder.getCallingUid();
+ if (!hasPermission(callingPackage)) {
+ throw new SecurityException(
+ "Caller does not have the permission needed to call this API; "
+ + "callingPackage=" + callingPackage
+ + ", callingUid=" + callingUid);
+ }
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+ userId, false /* allowAll */, false /* requireFull */,
+ "clearBroadcastResponseStats" /* name */, callingPackage);
+ mResponseStatsTracker.clearBroadcastEvents(callingUid, userId);
+ }
}
void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
index 819644846..0ec59c3 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastEvents.java
@@ -51,6 +51,10 @@
}
void onUidRemoved(int uid) {
+ clear(uid);
+ }
+
+ void clear(int uid) {
for (int i = mBroadcastEvents.size() - 1; i >= 0; --i) {
final LongSparseArray<BroadcastEvent> broadcastEvents = mBroadcastEvents.valueAt(i);
for (int j = broadcastEvents.size() - 1; j >= 0; --j) {
diff --git a/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java b/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
index ac2a320..1828a71 100644
--- a/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
+++ b/services/usage/java/com/android/server/usage/UserBroadcastResponseStats.java
@@ -16,6 +16,7 @@
package com.android.server.usage;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.BroadcastResponseStats;
@@ -23,6 +24,8 @@
import com.android.internal.util.IndentingPrintWriter;
+import java.util.List;
+
class UserBroadcastResponseStats {
/**
* Contains the mapping of a BroadcastEvent type to it's aggregated stats.
@@ -39,31 +42,38 @@
BroadcastEvent broadcastEvent) {
BroadcastResponseStats responseStats = mResponseStats.get(broadcastEvent);
if (responseStats == null) {
- responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage());
+ responseStats = new BroadcastResponseStats(broadcastEvent.getTargetPackage(),
+ broadcastEvent.getIdForResponseEvent());
mResponseStats.put(broadcastEvent, responseStats);
}
return responseStats;
}
- void aggregateBroadcastResponseStats(
- @NonNull BroadcastResponseStats responseStats,
- @NonNull String packageName, long id) {
+ void populateAllBroadcastResponseStats(
+ @NonNull List<BroadcastResponseStats> broadcastResponseStatsList,
+ @Nullable String packageName, @IntRange(from = 0) long id) {
for (int i = mResponseStats.size() - 1; i >= 0; --i) {
final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
- if (broadcastEvent.getIdForResponseEvent() == id
- && broadcastEvent.getTargetPackage().equals(packageName)) {
- responseStats.addCounts(mResponseStats.valueAt(i));
+ if (id != 0 && id != broadcastEvent.getIdForResponseEvent()) {
+ continue;
}
+ if (packageName != null && !packageName.equals(broadcastEvent.getTargetPackage())) {
+ continue;
+ }
+ broadcastResponseStatsList.add(mResponseStats.valueAt(i));
}
}
- void clearBroadcastResponseStats(@NonNull String packageName, long id) {
+ void clearBroadcastResponseStats(@Nullable String packageName, @IntRange(from = 0) long id) {
for (int i = mResponseStats.size() - 1; i >= 0; --i) {
final BroadcastEvent broadcastEvent = mResponseStats.keyAt(i);
- if (broadcastEvent.getIdForResponseEvent() == id
- && broadcastEvent.getTargetPackage().equals(packageName)) {
- mResponseStats.removeAt(i);
+ if (id != 0 && id != broadcastEvent.getIdForResponseEvent()) {
+ continue;
}
+ if (packageName != null && !packageName.equals(broadcastEvent.getTargetPackage())) {
+ continue;
+ }
+ mResponseStats.removeAt(i);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3d00474..eb3df1c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8226,7 +8226,9 @@
"store_sim_pin_for_unattended_reboot_bool";
/**
- * Determine whether "Enable 2G" toggle can be shown.
+ * Allow whether the user can use the "Allow 2G" toggle in Settings.
+ *
+ * If {@code true} then the toggle is disabled (i.e. grayed out).
*
* Used to trade privacy/security against potentially reduced carrier coverage for some
* carriers.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a49a61b5..b6ae530 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -25,15 +25,20 @@
import android.annotation.SystemApi;
import android.app.Activity;
import android.app.PendingIntent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccCardManager.ResetOption;
+import android.util.Log;
import com.android.internal.telephony.euicc.IEuiccController;
@@ -57,6 +62,7 @@
*/
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
public class EuiccManager {
+ private static final String TAG = "EuiccManager";
/**
* Intent action to launch the embedded SIM (eUICC) management settings screen.
@@ -811,6 +817,14 @@
*/
public static final int ERROR_INVALID_PORT = 10017;
+ /**
+ * Apps targeting on Android T and beyond will get exception whenever switchToSubscription
+ * without portIndex is called for disable subscription.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE = 218393363L;
private final Context mContext;
private int mCardId;
@@ -1127,7 +1141,7 @@
* intent to prompt the user to accept the download. The caller should also be authorized to
* manage the subscription to be enabled.
*
- * <p> From Android T, devices might support MEP(Multiple Enabled Profile), the subscription
+ * <p> From Android T, devices might support MEP(Multiple Enabled Profiles), the subscription
* can be installed on different port from the eUICC. Calling apps with carrier privilege
* (see {@link TelephonyManager#hasCarrierPrivileges}) over the currently active subscriptions
* can use {@link #switchToSubscription(int, int, PendingIntent)} to specify which port to
@@ -1138,10 +1152,12 @@
*
* @param subscriptionId the ID of the subscription to enable. May be
* {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
- * current profile without activating another profile to replace it. If it's a disable
- * operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
- * permission, or the calling app must be authorized to manage the active subscription on
- * the target eUICC.
+ * current profile without activating another profile to replace it. Calling apps targeting
+ * on android T must use {@link #switchToSubscription(int, int, PendingIntent)} API for
+ * disable profile, port index can be found from {@link SubscriptionInfo#getPortIndex()}.
+ * If it's a disable operation, requires the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or the
+ * calling app must be authorized to manage the active subscription on the target eUICC.
* @param callbackIntent a PendingIntent to launch when the operation completes.
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
@@ -1151,6 +1167,18 @@
return;
}
try {
+ // TODO: Uncomment below compat change code once callers are ported to use
+ // switchToSubscription with portIndex for disable operation.
+ // if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ // && getIEuiccController().isCompatChangeEnabled(mContext.getOpPackageName(),
+ // SWITCH_WITHOUT_PORT_INDEX_EXCEPTION_ON_DISABLE)) {
+ // // Apps targeting on Android T and beyond will get exception whenever
+ // // switchToSubscription without portIndex is called with INVALID_SUBSCRIPTION_ID.
+ // Log.e(TAG, "switchToSubscription without portIndex is not allowed for"
+ // + " disable operation");
+ // throw new IllegalArgumentException("Must use switchToSubscription with portIndex"
+ // + " API for disable operation");
+ // }
getIEuiccController().switchToSubscription(mCardId,
subscriptionId, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
@@ -1180,7 +1208,10 @@
* current profile without activating another profile to replace it. If it's a disable
* operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
* permission, or the calling app must be authorized to manage the active subscription on
- * the target eUICC.
+ * the target eUICC. From Android T, multiple enabled profiles is supported. Calling apps
+ * targeting on android T must use {@link #switchToSubscription(int, int, PendingIntent)}
+ * API for disable profile, port index can be found from
+ * {@link SubscriptionInfo#getPortIndex()}.
* @param portIndex the index of the port to target for the enabled subscription
* @param callbackIntent a PendingIntent to launch when the operation completes.
*/
@@ -1192,6 +1223,17 @@
return;
}
try {
+ boolean canWriteEmbeddedSubscriptions = mContext.checkCallingOrSelfPermission(
+ Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ // If the caller is not privileged caller and does not have the carrier privilege over
+ // any active subscription, do not continue.
+ if (!canWriteEmbeddedSubscriptions && !getIEuiccController()
+ .hasCarrierPrivilegesForPackageOnAnyPhone(mContext.getOpPackageName())) {
+ Log.e(TAG, "Not permitted to use switchToSubscription with portIndex");
+ throw new SecurityException(
+ "Must have carrier privileges to use switchToSubscription with portIndex");
+ }
getIEuiccController().switchToSubscriptionWithPort(mCardId,
subscriptionId, portIndex, mContext.getOpPackageName(), callbackIntent);
} catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index dda95b1..19f1a5b 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -55,4 +55,6 @@
List<String> getSupportedCountries(boolean isSupported);
boolean isSupportedCountry(String countryIso);
boolean isSimPortAvailable(int cardId, int portIndex, String callingPackage);
+ boolean hasCarrierPrivilegesForPackageOnAnyPhone(String callingPackage);
+ boolean isCompatChangeEnabled(String callingPackage, long changeId);
}
diff --git a/tests/Internal/src/com/android/internal/util/UserIconsTest.java b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
new file mode 100644
index 0000000..cc7b20b
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UserIconsTest {
+
+ @Test
+ public void convertToBitmapAtUserIconSize_sizeIsCorrect() {
+ Resources res = InstrumentationRegistry.getTargetContext().getResources();
+ Drawable icon = UserIcons.getDefaultUserIcon(res, 0, true);
+ Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, icon);
+ int expectedSize = res.getDimensionPixelSize(R.dimen.user_icon_size);
+
+ assertThat(bitmap.getWidth()).isEqualTo(expectedSize);
+ assertThat(bitmap.getHeight()).isEqualTo(expectedSize);
+ }
+
+}