Merge "Store PeopleSpaceTile in-memory" into sc-dev
diff --git a/Android.bp b/Android.bp
index acf86a0..0ffafc6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -326,6 +326,7 @@
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-os",
+ "framework-permission-aidl-java",
],
}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index f96fc83..b62ece6 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -236,7 +236,21 @@
* @hide
*/
public static final int REASON_BLUETOOTH_BROADCAST = 203;
-
+ /**
+ * Broadcast {@link android.content.Intent#ACTION_TIMEZONE_CHANGED}
+ * @hide
+ */
+ public static final int REASON_TIMEZONE_CHANGED = 204;
+ /**
+ * Broadcast {@link android.content.Intent#ACTION_TIME_CHANGED}
+ * @hide
+ */
+ public static final int REASON_TIME_CHANGED = 205;
+ /**
+ * Broadcast {@link android.content.Intent#ACTION_LOCALE_CHANGED}
+ * @hide
+ */
+ public static final int REASON_LOCALE_CHANGED = 206;
/* Reason code range 300-399 are reserved for other internal reasons */
/**
* Device idle system allow list, including EXCEPT-IDLE
@@ -369,6 +383,9 @@
REASON_PRE_BOOT_COMPLETED,
REASON_LOCKED_BOOT_COMPLETED,
REASON_BLUETOOTH_BROADCAST,
+ REASON_TIMEZONE_CHANGED,
+ REASON_TIME_CHANGED,
+ REASON_LOCALE_CHANGED,
REASON_SYSTEM_ALLOW_LISTED,
REASON_ALARM_MANAGER_ALARM_CLOCK,
REASON_ALARM_MANAGER_WHILE_IDLE,
@@ -641,6 +658,12 @@
return "LOCKED_BOOT_COMPLETED";
case REASON_BLUETOOTH_BROADCAST:
return "BLUETOOTH_BROADCAST";
+ case REASON_TIMEZONE_CHANGED:
+ return "TIMEZONE_CHANGED";
+ case REASON_TIME_CHANGED:
+ return "TIME_CHANGED";
+ case REASON_LOCALE_CHANGED:
+ return "LOCALE_CHANGED";
case REASON_SYSTEM_ALLOW_LISTED:
return "SYSTEM_ALLOW_LISTED";
case REASON_ALARM_MANAGER_ALARM_CLOCK:
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 64686a1..70d0d5d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -30,6 +30,7 @@
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.REASON_ALARM_MANAGER_WHILE_IDLE;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
@@ -81,6 +82,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelableException;
+import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -118,6 +120,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.os.BinderDeathDispatcher;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
@@ -202,6 +205,8 @@
.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ private static final BinderDeathDispatcher<IAlarmListener> sListenerDeathDispatcher =
+ new BinderDeathDispatcher<>();
final LocalLog mLog = new LocalLog(TAG);
AppOpsManager mAppOps;
@@ -292,6 +297,7 @@
BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic();
+ BroadcastOptions mOptsTimeBroadcast = BroadcastOptions.makeBasic();
// TODO(b/172085676): Move inside alarm store.
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
@@ -1786,7 +1792,12 @@
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
intent.putExtra(Intent.EXTRA_TIMEZONE, zone.getID());
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ mOptsTimeBroadcast.setTemporaryAppAllowlist(
+ mActivityManagerInternal.getBootTimeTempAllowListDuration(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_TIMEZONE_CHANGED, "");
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+ null /* receiverPermission */, mOptsTimeBroadcast.toBundle());
}
}
@@ -1809,9 +1820,8 @@
}
if (directReceiver != null) {
- try {
- directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0);
- } catch (RemoteException e) {
+ if (sListenerDeathDispatcher.linkToDeath(directReceiver, mListenerDeathRecipient)
+ <= 0) {
Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag);
return;
}
@@ -2824,6 +2834,12 @@
pw.println();
}
+ pw.println("Listener death dispatcher state:");
+ pw.increaseIndent();
+ sListenerDeathDispatcher.dump(pw);
+ pw.println();
+ pw.decreaseIndent();
+
if (mLog.dump(pw, "Recent problems:")) {
pw.println();
}
@@ -3929,8 +3945,12 @@
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
-
+ mOptsTimeBroadcast.setTemporaryAppAllowlist(
+ mActivityManagerInternal.getBootTimeTempAllowListDuration(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_TIME_CHANGED, "");
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+ null /* receiverPermission */, mOptsTimeBroadcast.toBundle());
// The world has changed on us, so we need to re-evaluate alarms
// regardless of whether the kernel has told us one went off.
result |= IS_WAKEUP_MASK;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
index 3c4961a..140cca6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -56,7 +56,7 @@
private boolean mDockIdle;
private boolean mProjectionActive;
private IdlenessListener mIdleListener;
- private final UiModeManager.OnProjectionStateChangeListener mOnProjectionStateChangeListener =
+ private final UiModeManager.OnProjectionStateChangedListener mOnProjectionStateChangedListener =
this::onProjectionStateChanged;
private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
@@ -105,9 +105,9 @@
// TODO(b/172579710): Move the callbacks off the main executor and on to
// JobSchedulerBackgroundThread.getExecutor() once synchronization is fixed in this class.
- context.getSystemService(UiModeManager.class).addOnProjectionStateChangeListener(
+ context.getSystemService(UiModeManager.class).addOnProjectionStateChangedListener(
UiModeManager.PROJECTION_TYPE_ALL, context.getMainExecutor(),
- mOnProjectionStateChangeListener);
+ mOnProjectionStateChangedListener);
}
private void onProjectionStateChanged(@UiModeManager.ProjectionType int activeProjectionTypes,
diff --git a/core/api/current.txt b/core/api/current.txt
index c2cb78e..6290d73 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1821,7 +1821,6 @@
field public static final int notification_large_icon_width = 17104901; // 0x1050005
field public static final int system_app_widget_background_radius;
field public static final int system_app_widget_inner_radius;
- field public static final int system_app_widget_internal_padding;
field public static final int thumbnail_height = 17104897; // 0x1050001
field public static final int thumbnail_width = 17104898; // 0x1050002
}
@@ -32950,6 +32949,7 @@
field public static final android.print.PrintAttributes.MediaSize JPN_HAGAKI;
field public static final android.print.PrintAttributes.MediaSize JPN_KAHU;
field public static final android.print.PrintAttributes.MediaSize JPN_KAKU2;
+ field @NonNull public static final android.print.PrintAttributes.MediaSize JPN_OE_PHOTO_L;
field public static final android.print.PrintAttributes.MediaSize JPN_OUFUKU;
field public static final android.print.PrintAttributes.MediaSize JPN_YOU4;
field @NonNull public static final android.print.PrintAttributes.MediaSize NA_ARCH_A;
@@ -32971,7 +32971,6 @@
field public static final android.print.PrintAttributes.MediaSize NA_QUARTO;
field @NonNull public static final android.print.PrintAttributes.MediaSize NA_SUPER_B;
field public static final android.print.PrintAttributes.MediaSize NA_TABLOID;
- field @NonNull public static final android.print.PrintAttributes.MediaSize OE_PHOTO_L;
field public static final android.print.PrintAttributes.MediaSize OM_DAI_PA_KAI;
field public static final android.print.PrintAttributes.MediaSize OM_JUURO_KU_KAI;
field public static final android.print.PrintAttributes.MediaSize OM_PA_KAI;
diff --git a/core/api/removed.txt b/core/api/removed.txt
index cdaa5f53..8229019a 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -1,4 +1,12 @@
// Signature format: 2.0
+package android {
+
+ public static final class R.dimen {
+ field public static final int __removed_system_app_widget_internal_padding;
+ }
+
+}
+
package android.app {
public class Notification implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bffd0e7..e80615a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -838,12 +838,12 @@
}
public class UiModeManager {
- method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void addOnProjectionStateChangeListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.app.UiModeManager.OnProjectionStateChangeListener);
+ method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void addOnProjectionStateChangedListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.app.UiModeManager.OnProjectionStateChangedListener);
method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int);
method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public int getActiveProjectionTypes();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public java.util.Set<java.lang.String> getProjectingPackages(int);
method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean releaseProjection(int);
- method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void removeOnProjectionStateChangeListener(@NonNull android.app.UiModeManager.OnProjectionStateChangeListener);
+ method @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public void removeOnProjectionStateChangedListener(@NonNull android.app.UiModeManager.OnProjectionStateChangedListener);
method @RequiresPermission(value=android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional=true) public boolean requestProjection(int);
field public static final String ACTION_ENTER_CAR_MODE_PRIORITIZED = "android.app.action.ENTER_CAR_MODE_PRIORITIZED";
field public static final String ACTION_EXIT_CAR_MODE_PRIORITIZED = "android.app.action.EXIT_CAR_MODE_PRIORITIZED";
@@ -855,7 +855,7 @@
field public static final int PROJECTION_TYPE_NONE = 0; // 0x0
}
- public static interface UiModeManager.OnProjectionStateChangeListener {
+ public static interface UiModeManager.OnProjectionStateChangedListener {
method public void onProjectionStateChanged(int, @NonNull java.util.Set<java.lang.String>);
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1967993..d366e35d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -219,6 +219,7 @@
public class AppOpsManager {
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
+ method public static void collectNotedOpSync(@NonNull android.app.SyncNotedAppOp);
method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
method public static int getNumOps();
method public boolean isOperationActive(int, int, String);
@@ -360,6 +361,10 @@
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
+ public final class SyncNotedAppOp implements android.os.Parcelable {
+ ctor public SyncNotedAppOp(int, @IntRange(from=0L) int, @Nullable String, @NonNull String);
+ }
+
public class TaskInfo {
method public boolean containsLaunchCookie(@NonNull android.os.IBinder);
method @NonNull public android.content.res.Configuration getConfiguration();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 75a38c2..4975fc2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2783,14 +2783,24 @@
private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
/**
- * If a thread is currently executing a two-way binder transaction, this stores the op-codes of
- * the app-ops that were noted during this transaction.
+ * Optimization: we need to propagate to IPCs whether the current thread is collecting
+ * app ops but using only the thread local above is too slow as it requires a map lookup
+ * on every IPC. We add this static var that is lockless and stores an OR-ed mask of the
+ * thread id's currently collecting ops, thus reducing the map lookup to a simple bit
+ * operation except the extremely unlikely case when threads with overlapping id bits
+ * execute op collecting ops.
+ */
+ private static volatile long sThreadsListeningForOpNotedInBinderTransaction = 0L;
+
+ /**
+ * If a thread is currently executing a two-way binder transaction, this stores the
+ * ops that were noted blaming any app (the caller, the caller of the caller, etc).
*
* @see #getNotedOpCollectionMode
* @see #collectNotedOpSync
*/
- private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
- new ThreadLocal<>();
+ private static final ThreadLocal<ArrayMap<String, ArrayMap<String, long[]>>>
+ sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>();
/** Whether noting for an appop should be collected */
private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
@@ -6844,7 +6854,7 @@
writeLongSparseLongArrayToParcel(mAccessCount, parcel);
writeLongSparseLongArrayToParcel(mRejectCount, parcel);
writeLongSparseLongArrayToParcel(mAccessDuration, parcel);
- writeDiscreteAccessArrayToParcel(mDiscreteAccesses, parcel);
+ writeDiscreteAccessArrayToParcel(mDiscreteAccesses, parcel, flags);
}
@Override
@@ -8105,7 +8115,7 @@
SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
- if (syncOp.getOpMode()== MODE_ALLOWED) {
+ if (syncOp.getOpMode() == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
collectNotedOpForSelf(syncOp);
} else if (collectionMode == COLLECT_SYNC) {
@@ -8872,70 +8882,11 @@
* @hide
*/
public static void startNotedAppOpsCollection(int callingUid) {
+ sThreadsListeningForOpNotedInBinderTransaction |= Thread.currentThread().getId();
sBinderThreadCallingUid.set(callingUid);
}
/**
- * State of a temporarily paused noted app-ops collection.
- *
- * @see #pauseNotedAppOpsCollection()
- *
- * @hide
- */
- public static class PausedNotedAppOpsCollection {
- final int mUid;
- final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps;
-
- PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String,
- long[]> collectedNotedAppOps) {
- mUid = uid;
- mCollectedNotedAppOps = collectedNotedAppOps;
- }
- }
-
- /**
- * Temporarily suspend collection of noted app-ops when binder-thread calls into the other
- * process. During such a call there might be call-backs coming back on the same thread which
- * should not be accounted to the current collection.
- *
- * @return a state needed to resume the collection
- *
- * @hide
- */
- public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
- Integer previousUid = sBinderThreadCallingUid.get();
- if (previousUid != null) {
- ArrayMap<String, long[]> previousCollectedNotedAppOps =
- sAppOpsNotedInThisBinderTransaction.get();
-
- sBinderThreadCallingUid.remove();
- sAppOpsNotedInThisBinderTransaction.remove();
-
- return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps);
- }
-
- return null;
- }
-
- /**
- * Resume a collection paused via {@link #pauseNotedAppOpsCollection}.
- *
- * @param prevCollection The state of the previous collection
- *
- * @hide
- */
- public static void resumeNotedAppOpsCollection(
- @Nullable PausedNotedAppOpsCollection prevCollection) {
- if (prevCollection != null) {
- sBinderThreadCallingUid.set(prevCollection.mUid);
-
- if (prevCollection.mCollectedNotedAppOps != null) {
- sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps);
- }
- }
- }
-
- /**
* Finish collection of noted appops on this thread.
*
* <p>Called at the end of a two way binder transaction.
@@ -8946,6 +8897,7 @@
*/
public static void finishNotedAppOpsCollection() {
sBinderThreadCallingUid.remove();
+ sThreadsListeningForOpNotedInBinderTransaction &= ~Thread.currentThread().getId();
sAppOpsNotedInThisBinderTransaction.remove();
}
@@ -8970,28 +8922,52 @@
* <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
*
* @param syncOp the op and attribution tag to note for
+ *
+ * @hide
*/
- private void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
+ @TestApi
+ public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
+ collectNotedOpSync(sOpStrToOp.get(syncOp.getOp()), syncOp.getAttributionTag(),
+ syncOp.getPackageName());
+ }
+
+ /**
+ * Collect a noted op when inside of a two-way binder call.
+ *
+ * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
+ *
+ * @param code the op code to note for
+ * @param attributionTag the attribution tag to note for
+ * @param packageName the package to note for
+ */
+ private static void collectNotedOpSync(int code, @Nullable String attributionTag,
+ @NonNull String packageName) {
// If this is inside of a two-way binder call:
// We are inside of a two-way binder call. Delivered to caller via
// {@link #prefixParcelWithAppOpsIfNeeded}
- int op = sOpStrToOp.get(syncOp.getOp());
- ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
+ ArrayMap<String, ArrayMap<String, long[]>> appOpsNoted =
+ sAppOpsNotedInThisBinderTransaction.get();
if (appOpsNoted == null) {
appOpsNoted = new ArrayMap<>(1);
sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
}
- long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag());
- if (appOpsNotedForAttribution == null) {
- appOpsNotedForAttribution = new long[2];
- appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution);
+ ArrayMap<String, long[]> packageAppOpsNotedForAttribution = appOpsNoted.get(packageName);
+ if (packageAppOpsNotedForAttribution == null) {
+ packageAppOpsNotedForAttribution = new ArrayMap<>(1);
+ appOpsNoted.put(packageName, packageAppOpsNotedForAttribution);
}
- if (op < 64) {
- appOpsNotedForAttribution[0] |= 1L << op;
+ long[] appOpsNotedForAttribution = packageAppOpsNotedForAttribution.get(attributionTag);
+ if (appOpsNotedForAttribution == null) {
+ appOpsNotedForAttribution = new long[2];
+ packageAppOpsNotedForAttribution.put(attributionTag, appOpsNotedForAttribution);
+ }
+
+ if (code < 64) {
+ appOpsNotedForAttribution[0] |= 1L << code;
} else {
- appOpsNotedForAttribution[1] |= 1L << (op - 64);
+ appOpsNotedForAttribution[1] |= 1L << (code - 64);
}
}
@@ -9045,9 +9021,7 @@
}
}
- Integer binderUid = sBinderThreadCallingUid.get();
-
- if (binderUid != null && binderUid == uid) {
+ if (isListeningForOpNotedInBinderTransaction()) {
return COLLECT_SYNC;
} else {
return COLLECT_ASYNC;
@@ -9064,21 +9038,31 @@
*
* @hide
*/
+ // TODO (b/186872903) Refactor how sync noted ops are propagaged.
public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
- ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
+ final ArrayMap<String, ArrayMap<String, long[]>> notedAppOps =
+ sAppOpsNotedInThisBinderTransaction.get();
if (notedAppOps == null) {
return;
}
p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
- int numAttributionWithNotesAppOps = notedAppOps.size();
- p.writeInt(numAttributionWithNotesAppOps);
+ final int packageCount = notedAppOps.size();
+ p.writeInt(packageCount);
- for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
+ for (int i = 0; i < packageCount; i++) {
p.writeString(notedAppOps.keyAt(i));
- p.writeLong(notedAppOps.valueAt(i)[0]);
- p.writeLong(notedAppOps.valueAt(i)[1]);
+
+ final ArrayMap<String, long[]> notedTagAppOps = notedAppOps.valueAt(i);
+ final int tagCount = notedTagAppOps.size();
+ p.writeInt(tagCount);
+
+ for (int j = 0; j < tagCount; j++) {
+ p.writeString(notedTagAppOps.keyAt(j));
+ p.writeLong(notedTagAppOps.valueAt(j)[0]);
+ p.writeLong(notedTagAppOps.valueAt(j)[1]);
+ }
}
}
@@ -9093,36 +9077,57 @@
* @hide
*/
public static void readAndLogNotedAppops(@NonNull Parcel p) {
- int numAttributionsWithNotedAppOps = p.readInt();
+ final int packageCount = p.readInt();
+ if (packageCount <= 0) {
+ return;
+ }
- for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
- String attributionTag = p.readString();
- long[] rawNotedAppOps = new long[2];
- rawNotedAppOps[0] = p.readLong();
- rawNotedAppOps[1] = p.readLong();
+ final String myPackageName = ActivityThread.currentPackageName();
+ if (myPackageName == null) {
+ return;
+ }
- if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
- BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
+ synchronized (sLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final String packageName = p.readString();
- synchronized (sLock) {
+ final int tagCount = p.readInt();
+ for (int j = 0; j < tagCount; j++) {
+ final String attributionTag = p.readString();
+ final long[] rawNotedAppOps = new long[2];
+ rawNotedAppOps[0] = p.readLong();
+ rawNotedAppOps[1] = p.readLong();
+
+ if (rawNotedAppOps[0] == 0 && rawNotedAppOps[1] == 0) {
+ continue;
+ }
+
+ final BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
for (int code = notedAppOps.nextSetBit(0); code != -1;
code = notedAppOps.nextSetBit(code + 1)) {
- if (sOnOpNotedCallback != null) {
- sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
- } else {
- String message = getFormattedStackTrace();
- sUnforwardedOps.add(
- new AsyncNotedAppOp(code, Process.myUid(), attributionTag,
- message, System.currentTimeMillis()));
- if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
- sUnforwardedOps.remove(0);
+ if (myPackageName.equals(packageName)) {
+ if (sOnOpNotedCallback != null) {
+ sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code,
+ attributionTag, packageName));
+ } else {
+ String message = getFormattedStackTrace();
+ sUnforwardedOps.add(new AsyncNotedAppOp(code, Process.myUid(),
+ attributionTag, message, System.currentTimeMillis()));
+ if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+ sUnforwardedOps.remove(0);
+ }
}
+ } else if (isListeningForOpNotedInBinderTransaction()) {
+ collectNotedOpSync(code, attributionTag, packageName);
}
}
- }
- for (int code = notedAppOps.nextSetBit(0); code != -1;
- code = notedAppOps.nextSetBit(code + 1)) {
- sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
+ for (int code = notedAppOps.nextSetBit(0); code != -1;
+ code = notedAppOps.nextSetBit(code + 1)) {
+ if (myPackageName.equals(packageName)) {
+ sMessageCollector.onNoted(new SyncNotedAppOp(code,
+ attributionTag, packageName));
+ }
+ }
}
}
}
@@ -9229,7 +9234,17 @@
* @hide
*/
public static boolean isListeningForOpNoted() {
- return sOnOpNotedCallback != null || isCollectingStackTraces();
+ return sOnOpNotedCallback != null || isListeningForOpNotedInBinderTransaction()
+ || isCollectingStackTraces();
+ }
+
+ /**
+ * @return whether we are in a binder transaction and collecting appops.
+ */
+ private static boolean isListeningForOpNotedInBinderTransaction() {
+ return (sThreadsListeningForOpNotedInBinderTransaction
+ & Thread.currentThread().getId()) != 0
+ && sBinderThreadCallingUid.get() != null;
}
/**
@@ -9696,29 +9711,16 @@
}
private static void writeDiscreteAccessArrayToParcel(
- @Nullable List<AttributedOpEntry> array, @NonNull Parcel parcel) {
- if (array != null) {
- final int size = array.size();
- parcel.writeInt(size);
- for (int i = 0; i < size; i++) {
- array.get(i).writeToParcel(parcel, 0);
- }
- } else {
- parcel.writeInt(-1);
- }
+ @Nullable List<AttributedOpEntry> array, @NonNull Parcel parcel, int flags) {
+ ParceledListSlice<AttributedOpEntry> listSlice =
+ array == null ? null : new ParceledListSlice<>(array);
+ parcel.writeParcelable(listSlice, flags);
}
private static @Nullable List<AttributedOpEntry> readDiscreteAccessArrayFromParcel(
@NonNull Parcel parcel) {
- final int size = parcel.readInt();
- if (size < 0) {
- return null;
- }
- final List<AttributedOpEntry> array = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- array.add(new AttributedOpEntry(parcel));
- }
- return array;
+ final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null);
+ return listSlice == null ? null : listSlice.getList();
}
/**
diff --git a/core/java/android/app/IOnProjectionStateChangeListener.aidl b/core/java/android/app/IOnProjectionStateChangedListener.aidl
similarity index 93%
rename from core/java/android/app/IOnProjectionStateChangeListener.aidl
rename to core/java/android/app/IOnProjectionStateChangedListener.aidl
index f154985..2d2bf7f 100644
--- a/core/java/android/app/IOnProjectionStateChangeListener.aidl
+++ b/core/java/android/app/IOnProjectionStateChangedListener.aidl
@@ -17,6 +17,6 @@
package android.app;
/** {@hide} */
-oneway interface IOnProjectionStateChangeListener {
+oneway interface IOnProjectionStateChangedListener {
void onProjectionStateChanged(int activeProjectionTypes, in List<String> projectingPackages);
}
\ No newline at end of file
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index f71eebdc..9f21bcc 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -16,7 +16,7 @@
package android.app;
-import android.app.IOnProjectionStateChangeListener;
+import android.app.IOnProjectionStateChangedListener;
/**
* Interface used to control special UI modes.
@@ -119,12 +119,12 @@
/**
* Registers a listener for changes to projection state.
*/
- void addOnProjectionStateChangeListener(in IOnProjectionStateChangeListener listener, int projectionType);
+ void addOnProjectionStateChangedListener(in IOnProjectionStateChangedListener listener, int projectionType);
/**
* Unregisters a listener for changes to projection state.
*/
- void removeOnProjectionStateChangeListener(in IOnProjectionStateChangeListener listener);
+ void removeOnProjectionStateChangedListener(in IOnProjectionStateChangedListener listener);
/**
* Returns packages that have currently set the given projection type.
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index bc4e543..32d889e 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -19,7 +19,9 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.os.Parcelable;
+import android.os.Process;
import com.android.internal.annotations.Immutable;
import com.android.internal.util.DataClass;
@@ -48,13 +50,19 @@
private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
/** attributionTag of synchronous appop noted */
private final @Nullable String mAttributionTag;
+ /**
+ * The package this op applies to
+ * @hide
+ */
+ private final @NonNull String mPackageName;
/**
* Native code relies on parcel ordering, do not change
* @hide
*/
+ @TestApi
public SyncNotedAppOp(int opMode, @IntRange(from = 0L) int opCode,
- @Nullable String attributionTag) {
+ @Nullable String attributionTag, @NonNull String packageName) {
this.mOpCode = opCode;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mOpCode,
@@ -62,6 +70,7 @@
"to", AppOpsManager._NUM_OP - 1);
this.mAttributionTag = attributionTag;
this.mOpMode = opMode;
+ this.mPackageName = packageName;
}
/**
@@ -73,7 +82,25 @@
* attributionTag of synchronous appop noted
*/
public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag) {
- this(AppOpsManager.MODE_IGNORED, opCode, attributionTag);
+ this(AppOpsManager.MODE_IGNORED, opCode, attributionTag, ActivityThread
+ .currentPackageName());
+ }
+
+ /**
+ * Creates a new SyncNotedAppOp.
+ *
+ * @param opCode
+ * op code of synchronous appop noted
+ * @param attributionTag
+ * attributionTag of synchronous appop noted
+ * @param packageName
+ * The package this op applies to
+ *
+ * @hide
+ */
+ public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String attributionTag,
+ @NonNull String packageName) {
+ this(AppOpsManager.MODE_IGNORED, opCode, attributionTag, packageName);
}
/**
@@ -113,6 +140,16 @@
return mAttributionTag;
}
+ /**
+ * The package this op applies to
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
@Override
@DataClass.Generated.Member
public boolean equals(@Nullable Object o) {
@@ -128,7 +165,8 @@
return true
&& mOpMode == that.mOpMode
&& mOpCode == that.mOpCode
- && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
+ && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+ && java.util.Objects.equals(mPackageName, that.mPackageName);
}
@Override
@@ -141,6 +179,7 @@
_hash = 31 * _hash + mOpMode;
_hash = 31 * _hash + mOpCode;
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
return _hash;
}
@@ -156,6 +195,7 @@
dest.writeInt(mOpMode);
dest.writeInt(mOpCode);
if (mAttributionTag != null) dest.writeString(mAttributionTag);
+ dest.writeString(mPackageName);
}
@Override
@@ -173,6 +213,7 @@
int opMode = in.readInt();
int opCode = in.readInt();
String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
+ String packageName = in.readString();
this.mOpMode = opMode;
this.mOpCode = opCode;
@@ -181,6 +222,9 @@
"from", 0L,
"to", AppOpsManager._NUM_OP - 1);
this.mAttributionTag = attributionTag;
+ this.mPackageName = packageName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
// onConstructed(); // You can define this method to get a callback
}
@@ -200,10 +244,10 @@
};
@DataClass.Generated(
- time = 1617317997768L,
+ time = 1619711733947L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
- inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false)")
+ inputSignatures = "private final int mOpMode\nprivate final @android.annotation.IntRange int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mAttributionTag\nprivate final @android.annotation.NonNull java.lang.String mPackageName\npublic @android.annotation.NonNull java.lang.String getOp()\npublic int getOpMode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 24fd04b..973a8fb 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -79,7 +79,7 @@
* @hide
*/
@SystemApi
- public interface OnProjectionStateChangeListener {
+ public interface OnProjectionStateChangedListener {
/**
* Callback invoked when projection state changes for a {@link ProjectionType} for which
* this listener was added.
@@ -254,10 +254,10 @@
private final Object mLock = new Object();
/**
* Map that stores internally created {@link InnerListener} objects keyed by their corresponding
- * externally provided {@link OnProjectionStateChangeListener} objects.
+ * externally provided callback objects.
*/
@GuardedBy("mLock")
- private final Map<OnProjectionStateChangeListener, InnerListener>
+ private final Map<OnProjectionStateChangedListener, InnerListener>
mProjectionStateListenerMap = new ArrayMap<>();
/**
@@ -265,9 +265,9 @@
* fail to remove listeners.
*/
@GuardedBy("mLock")
- private final OnProjectionStateChangeListenerResourceManager
- mOnProjectionStateChangeListenerResourceManager =
- new OnProjectionStateChangeListenerResourceManager();
+ private final OnProjectionStateChangedListenerResourceManager
+ mOnProjectionStateChangedListenerResourceManager =
+ new OnProjectionStateChangedListenerResourceManager();
@UnsupportedAppUsage
/*package*/ UiModeManager() throws ServiceNotFoundException {
@@ -687,7 +687,7 @@
/**
* Indicates no projection type. Can be used to compare with the {@link ProjectionType} in
- * {@link OnProjectionStateChangeListener#onProjectionStateChanged(int, Set)}.
+ * {@link OnProjectionStateChangedListener#onProjectionStateChanged(int, Set)}.
*
* @hide
*/
@@ -706,7 +706,7 @@
public static final int PROJECTION_TYPE_AUTOMOTIVE = 0x0001;
/**
* Indicates all projection types. For use with
- * {@link #addOnProjectionStateChangeListener(int, Executor, OnProjectionStateChangeListener)}
+ * {@link #addOnProjectionStateChangedListener(int, Executor, OnProjectionStateChangedListener)}
* and {@link #getProjectingPackages(int)}.
*
* @hide
@@ -829,15 +829,15 @@
*
* @param projectionType one or more {@link ProjectionType}s to listen for changes regarding
* @param executor an {@link Executor} on which to invoke the callbacks
- * @param listener the {@link OnProjectionStateChangeListener} to add
+ * @param listener the {@link OnProjectionStateChangedListener} to add
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE)
- public void addOnProjectionStateChangeListener(@ProjectionType int projectionType,
+ public void addOnProjectionStateChangedListener(@ProjectionType int projectionType,
@NonNull @CallbackExecutor Executor executor,
- @NonNull OnProjectionStateChangeListener listener) {
+ @NonNull OnProjectionStateChangedListener listener) {
synchronized (mLock) {
if (mProjectionStateListenerMap.containsKey(listener)) {
Slog.i(TAG, "Attempted to add listener that was already added.");
@@ -845,12 +845,12 @@
}
if (mService != null) {
InnerListener innerListener = new InnerListener(executor, listener,
- mOnProjectionStateChangeListenerResourceManager);
+ mOnProjectionStateChangedListenerResourceManager);
try {
- mService.addOnProjectionStateChangeListener(innerListener, projectionType);
+ mService.addOnProjectionStateChangedListener(innerListener, projectionType);
mProjectionStateListenerMap.put(listener, innerListener);
} catch (RemoteException e) {
- mOnProjectionStateChangeListenerResourceManager.remove(innerListener);
+ mOnProjectionStateChangedListenerResourceManager.remove(innerListener);
throw e.rethrowFromSystemServer();
}
}
@@ -860,14 +860,14 @@
/**
* Removes the listener so it stops receiving updates for all {@link ProjectionType}s.
*
- * @param listener the {@link OnProjectionStateChangeListener} to remove
+ * @param listener the {@link OnProjectionStateChangedListener} to remove
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE)
- public void removeOnProjectionStateChangeListener(
- @NonNull OnProjectionStateChangeListener listener) {
+ public void removeOnProjectionStateChangedListener(
+ @NonNull OnProjectionStateChangedListener listener) {
synchronized (mLock) {
InnerListener innerListener = mProjectionStateListenerMap.get(listener);
if (innerListener == null) {
@@ -876,23 +876,23 @@
}
if (mService != null) {
try {
- mService.removeOnProjectionStateChangeListener(innerListener);
+ mService.removeOnProjectionStateChangedListener(innerListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mProjectionStateListenerMap.remove(listener);
- mOnProjectionStateChangeListenerResourceManager.remove(innerListener);
+ mOnProjectionStateChangedListenerResourceManager.remove(innerListener);
}
}
- private static class InnerListener extends IOnProjectionStateChangeListener.Stub {
- private final WeakReference<OnProjectionStateChangeListenerResourceManager>
+ private static class InnerListener extends IOnProjectionStateChangedListener.Stub {
+ private final WeakReference<OnProjectionStateChangedListenerResourceManager>
mResourceManager;
private InnerListener(@NonNull Executor executor,
- @NonNull OnProjectionStateChangeListener outerListener,
- @NonNull OnProjectionStateChangeListenerResourceManager resourceManager) {
+ @NonNull OnProjectionStateChangedListener outerListener,
+ @NonNull OnProjectionStateChangedListenerResourceManager resourceManager) {
resourceManager.put(this, executor, outerListener);
mResourceManager = new WeakReference<>(resourceManager);
}
@@ -900,13 +900,14 @@
@Override
public void onProjectionStateChanged(int activeProjectionTypes,
List<String> projectingPackages) {
- OnProjectionStateChangeListenerResourceManager resourceManager = mResourceManager.get();
+ OnProjectionStateChangedListenerResourceManager resourceManager =
+ mResourceManager.get();
if (resourceManager == null) {
Slog.w(TAG, "Can't execute onProjectionStateChanged, resource manager is gone.");
return;
}
- OnProjectionStateChangeListener outerListener = resourceManager.getOuterListener(this);
+ OnProjectionStateChangedListener outerListener = resourceManager.getOuterListener(this);
Executor executor = resourceManager.getExecutor(this);
if (outerListener == null || executor == null) {
Slog.w(TAG, "Can't execute onProjectionStatechanged, references are null.");
@@ -914,7 +915,7 @@
}
executor.execute(PooledLambda.obtainRunnable(
- OnProjectionStateChangeListener::onProjectionStateChanged,
+ OnProjectionStateChangedListener::onProjectionStateChanged,
outerListener,
activeProjectionTypes,
new ArraySet<>(projectingPackages)).recycleOnUse());
@@ -924,15 +925,15 @@
/**
* Wrapper class that ensures we don't leak {@link Activity} or other large {@link Context} in
* which this {@link UiModeManager} resides if/when it ends without unregistering associated
- * {@link OnProjectionStateChangeListener}s.
+ * callback objects.
*/
- private static class OnProjectionStateChangeListenerResourceManager {
- private final Map<InnerListener, OnProjectionStateChangeListener> mOuterListenerMap =
+ private static class OnProjectionStateChangedListenerResourceManager {
+ private final Map<InnerListener, OnProjectionStateChangedListener> mOuterListenerMap =
new ArrayMap<>(1);
private final Map<InnerListener, Executor> mExecutorMap = new ArrayMap<>(1);
void put(@NonNull InnerListener innerListener, @NonNull Executor executor,
- OnProjectionStateChangeListener outerListener) {
+ OnProjectionStateChangedListener outerListener) {
mOuterListenerMap.put(innerListener, outerListener);
mExecutorMap.put(innerListener, executor);
}
@@ -942,7 +943,7 @@
mExecutorMap.remove(innerListener);
}
- OnProjectionStateChangeListener getOuterListener(@NonNull InnerListener innerListener) {
+ OnProjectionStateChangedListener getOuterListener(@NonNull InnerListener innerListener) {
return mOuterListenerMap.get(innerListener);
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 82da4fb..823b7235 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -17,6 +17,7 @@
package android.appwidget;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
import android.compat.annotation.UnsupportedAppUsage;
@@ -68,6 +69,7 @@
static final String TAG = "AppWidgetHostView";
private static final String KEY_JAILED_ARRAY = "jail";
+ private static final String KEY_INFLATION_ID = "inflation_id";
static final boolean LOGD = false;
@@ -97,9 +99,12 @@
private RemoteViews.ColorResources mColorResources = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
+ private long mLastInflatedRemoteViewsId = -1;
private Executor mAsyncExecutor;
private CancellationSignal mLastExecutionSignal;
+ private SparseArray<Parcelable> mDelayedRestoredState;
+ private long mDelayedRestoredInflationId;
/**
* Create a host view. Uses default fade animations.
@@ -226,6 +231,8 @@
Bundle bundle = new Bundle();
bundle.putSparseParcelableArray(KEY_JAILED_ARRAY, jail);
+ bundle.putLong(KEY_INFLATION_ID, mLastInflatedRemoteViewsId);
+ container.put(generateId(), bundle);
container.put(generateId(), bundle);
}
@@ -239,14 +246,30 @@
final Parcelable parcelable = container.get(generateId());
SparseArray<Parcelable> jail = null;
+ long inflationId = -1;
if (parcelable instanceof Bundle) {
- jail = ((Bundle) parcelable).getSparseParcelableArray(KEY_JAILED_ARRAY);
+ Bundle bundle = (Bundle) parcelable;
+ jail = bundle.getSparseParcelableArray(KEY_JAILED_ARRAY);
+ inflationId = bundle.getLong(KEY_INFLATION_ID, -1);
}
if (jail == null) jail = new SparseArray<>();
+ mDelayedRestoredState = jail;
+ mDelayedRestoredInflationId = inflationId;
+ restoreInstanceState();
+ }
+
+ void restoreInstanceState() {
+ long inflationId = mDelayedRestoredInflationId;
+ SparseArray<Parcelable> state = mDelayedRestoredState;
+ if (inflationId == -1 || inflationId != mLastInflatedRemoteViewsId) {
+ return; // We don't restore.
+ }
+ mDelayedRestoredInflationId = -1;
+ mDelayedRestoredState = null;
try {
- super.dispatchRestoreInstanceState(jail);
+ super.dispatchRestoreInstanceState(state);
} catch (Exception e) {
Log.e(TAG, "failed to restoreInstanceState for widget id: " + mAppWidgetId + ", "
+ (mInfo == null ? "null" : mInfo.provider), e);
@@ -476,7 +499,7 @@
* AppWidget provider. Will animate into these new views as needed
*/
public void updateAppWidget(RemoteViews remoteViews) {
- this.mLastInflatedRemoteViews = remoteViews;
+ mLastInflatedRemoteViews = remoteViews;
applyRemoteViews(remoteViews, true);
}
@@ -484,17 +507,23 @@
* Reapply the last inflated remote views, or the default view is none was inflated.
*/
private void reapplyLastRemoteViews() {
+ SparseArray<Parcelable> savedState = new SparseArray<>();
+ saveHierarchyState(savedState);
applyRemoteViews(mLastInflatedRemoteViews, true);
+ restoreHierarchyState(savedState);
}
/**
* @hide
*/
- protected void applyRemoteViews(RemoteViews remoteViews, boolean useAsyncIfPossible) {
+ protected void applyRemoteViews(@Nullable RemoteViews remoteViews, boolean useAsyncIfPossible) {
boolean recycled = false;
View content = null;
Exception exception = null;
+ // Block state restore until the end of the apply.
+ mLastInflatedRemoteViewsId = -1;
+
if (mLastExecutionSignal != null) {
mLastExecutionSignal.cancel();
mLastExecutionSignal = null;
@@ -525,6 +554,7 @@
rvToApply.reapply(mContext, mView, mInteractionHandler, mCurrentSize,
mColorResources);
content = mView;
+ mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
recycled = true;
if (LOGD) Log.d(TAG, "was able to recycle existing layout");
} catch (RuntimeException e) {
@@ -537,6 +567,7 @@
try {
content = rvToApply.apply(mContext, this, mInteractionHandler,
mCurrentSize, mColorResources);
+ mLastInflatedRemoteViewsId = rvToApply.computeUniqueId(remoteViews);
if (LOGD) Log.d(TAG, "had to inflate new layout");
} catch (RuntimeException e) {
exception = e;
@@ -574,12 +605,16 @@
}
}
- private void inflateAsync(RemoteViews remoteViews) {
+ private void inflateAsync(@NonNull RemoteViews remoteViews) {
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
mRemoteContext = getRemoteContext();
int layoutId = remoteViews.getLayoutId();
+ if (mLastExecutionSignal != null) {
+ mLastExecutionSignal.cancel();
+ }
+
// If our stale view has been prepared to match active, and the new
// layout matches, try recycling it
if (layoutId == mLayoutId && mView != null) {
@@ -611,7 +646,10 @@
private final boolean mIsReapply;
private final int mLayoutId;
- public ViewApplyListener(RemoteViews views, int layoutId, boolean isReapply) {
+ ViewApplyListener(
+ RemoteViews views,
+ int layoutId,
+ boolean isReapply) {
mViews = views;
mLayoutId = layoutId;
mIsReapply = isReapply;
@@ -623,6 +661,10 @@
mViewMode = VIEW_MODE_CONTENT;
applyContent(v, mIsReapply, null);
+
+ mLastInflatedRemoteViewsId = mViews.computeUniqueId(mLastInflatedRemoteViews);
+ restoreInstanceState();
+ mLastExecutionSignal = null;
}
@Override
@@ -638,6 +680,7 @@
} else {
applyContent(null, false, e);
}
+ mLastExecutionSignal = null;
}
}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 3db1885..063ba11 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -455,13 +455,14 @@
@Nullable
public final CharSequence loadDescription(@NonNull Context context) {
if (ResourceId.isValid(descriptionRes)) {
- return context.getPackageManager()
- .getText(
+ CharSequence description =
+ context.getPackageManager().getText(
providerInfo.packageName,
descriptionRes,
- providerInfo.applicationInfo)
- .toString()
- .trim();
+ providerInfo.applicationInfo);
+ if (description != null) {
+ return description.toString().trim();
+ }
}
return null;
}
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 2c155d58..7ab731f 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -31,10 +31,9 @@
import android.util.ArraySet;
import com.android.internal.annotations.Immutable;
-import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Parcelling;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.Objects;
import java.util.Set;
@@ -70,10 +69,10 @@
* This is supported to handle cases where you don't have access to the caller's attribution
* source and you can directly use the {@link AttributionSource.Builder} APIs. However,
* if the data flows through more than two apps (more than you access the data for the
- * caller - which you cannot know ahead of time) you need to have a handle to the {@link
- * AttributionSource} for the calling app's context in order to create an attribution context.
- * This means you either need to have an API for the other app to send you its attribution
- * source or use a platform API that pipes the callers attribution source.
+ * caller) you need to have a handle to the {@link AttributionSource} for the calling app's
+ * context in order to create an attribution context. This means you either need to have an
+ * API for the other app to send you its attribution source or use a platform API that pipes
+ * the callers attribution source.
* <p>
* You cannot forge an attribution chain without the participation of every app in the
* attribution chain (aside of the special case mentioned above). To create an attribution
@@ -85,80 +84,11 @@
* permission protected APIs since some app in the chain may not have the permission.
*/
@Immutable
-// TODO: Codegen doesn't properly verify the class if the parcelling is inner class
-// TODO: Codegen doesn't allow overriding the constructor to change its visibility
-// TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi)
-// TODO: Codegen doesn't properly read/write IBinder members
-// TODO: Codegen doesn't properly handle Set arguments
-// TODO: Codegen requires @SystemApi annotations on fields which breaks
-// android.signature.cts.api.AnnotationTest (need to update the test)
-// @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true)
public final class AttributionSource implements Parcelable {
- /**
- * @hide
- */
- static class RenouncedPermissionsParcelling implements Parcelling<Set<String>> {
+ private final @NonNull AttributionSourceState mAttributionSourceState;
- @Override
- public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
- if (item == null) {
- dest.writeInt(-1);
- } else {
- dest.writeInt(item.size());
- for (String permission : item) {
- dest.writeString8(permission);
- }
- }
- }
-
- @Override
- public Set<String> unparcel(Parcel source) {
- final int size = source.readInt();
- if (size < 0) {
- return null;
- }
- final ArraySet<String> result = new ArraySet<>(size);
- for (int i = 0; i < size; i++) {
- result.add(source.readString8());
- }
- return result;
- }
- }
-
- /**
- * The UID that is accessing the permission protected data.
- */
- private final int mUid;
-
- /**
- * The package that is accessing the permission protected data.
- */
- private @Nullable String mPackageName = null;
-
- /**
- * The attribution tag of the app accessing the permission protected data.
- */
- private @Nullable String mAttributionTag = null;
-
- /**
- * Unique token for that source.
- *
- * @hide
- */
- private @Nullable IBinder mToken = null;
-
- /**
- * Permissions that should be considered revoked regardless if granted.
- *
- * @hide
- */
- @DataClass.ParcelWith(RenouncedPermissionsParcelling.class)
- private @Nullable Set<String> mRenouncedPermissions = null;
-
- /**
- * The next app to receive the permission protected data.
- */
- private @Nullable AttributionSource mNext = null;
+ private @Nullable AttributionSource mNextCached;
+ private @Nullable Set<String> mRenouncedPermissionsCached;
/** @hide */
@TestApi
@@ -171,8 +101,7 @@
@TestApi
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, /*token*/ null,
- /*renouncedPermissions*/ null, next);
+ this(uid, packageName, attributionTag, /*renouncedPermissions*/ null, next);
}
/** @hide */
@@ -180,8 +109,8 @@
public AttributionSource(int uid, @Nullable String packageName,
@Nullable String attributionTag, @Nullable Set<String> renouncedPermissions,
@Nullable AttributionSource next) {
- this(uid, packageName, attributionTag, /*token*/ null,
- renouncedPermissions, next);
+ this(uid, packageName, attributionTag, /*token*/ null, (renouncedPermissions != null)
+ ? renouncedPermissions.toArray(new String[0]) : null, next);
}
/** @hide */
@@ -191,16 +120,49 @@
/*token*/ null, /*renouncedPermissions*/ null, next);
}
+ AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
+ @Nullable IBinder token, @Nullable String[] renouncedPermissions,
+ @Nullable AttributionSource next) {
+ mAttributionSourceState = new AttributionSourceState();
+ mAttributionSourceState.uid = uid;
+ mAttributionSourceState.packageName = packageName;
+ mAttributionSourceState.attributionTag = attributionTag;
+ mAttributionSourceState.token = token;
+ mAttributionSourceState.renouncedPermissions = renouncedPermissions;
+ mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
+ {next.mAttributionSourceState} : null;
+ }
+
+ AttributionSource(@NonNull Parcel in) {
+ this(AttributionSourceState.CREATOR.createFromParcel(in));
+ }
+
+ /** @hide */
+ public AttributionSource(@NonNull AttributionSourceState attributionSourceState) {
+ mAttributionSourceState = attributionSourceState;
+ }
+
/** @hide */
public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
- return new AttributionSource(mUid, mPackageName, mAttributionTag, mToken,
- mRenouncedPermissions, next);
+ return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
+ getToken(), mAttributionSourceState.renouncedPermissions, next);
}
/** @hide */
public AttributionSource withToken(@Nullable IBinder token) {
- return new AttributionSource(mUid, mPackageName, mAttributionTag, token,
- mRenouncedPermissions, mNext);
+ return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
+ token, mAttributionSourceState.renouncedPermissions, getNext());
+ }
+
+ /** @hide */
+ public AttributionSource withPackageName(@Nullable String packageName) {
+ return new AttributionSource(getUid(), packageName, getAttributionTag(), getToken(),
+ mAttributionSourceState.renouncedPermissions, getNext());
+ }
+
+ /** @hide */
+ public @NonNull AttributionSourceState asState() {
+ return mAttributionSourceState;
}
/**
@@ -213,10 +175,9 @@
* from the caller.
*/
public void enforceCallingUid() {
- final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
- throw new SecurityException("Calling uid: " + callingUid
- + " doesn't match source uid: " + mUid);
+ if (!checkCallingUid()) {
+ throw new SecurityException("Calling uid: " + Binder.getCallingUid()
+ + " doesn't match source uid: " + mAttributionSourceState.uid);
}
// No need to check package as app ops manager does it already.
}
@@ -231,7 +192,8 @@
*/
public boolean checkCallingUid() {
final int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID && callingUid != mUid) {
+ if (callingUid != Process.SYSTEM_UID
+ && callingUid != mAttributionSourceState.uid) {
return false;
}
// No need to check package as app ops manager does it already.
@@ -242,11 +204,12 @@
public String toString() {
if (Build.IS_DEBUGGABLE) {
return "AttributionSource { " +
- "uid = " + mUid + ", " +
- "packageName = " + mPackageName + ", " +
- "attributionTag = " + mAttributionTag + ", " +
- "token = " + mToken + ", " +
- "next = " + mNext +
+ "uid = " + mAttributionSourceState.uid + ", " +
+ "packageName = " + mAttributionSourceState.packageName + ", " +
+ "attributionTag = " + mAttributionSourceState.attributionTag + ", " +
+ "token = " + mAttributionSourceState.token + ", " +
+ "next = " + (mAttributionSourceState.next != null
+ ? mAttributionSourceState.next[0]: null) +
" }";
}
return super.toString();
@@ -258,8 +221,8 @@
* @hide
*/
public int getNextUid() {
- if (mNext != null) {
- return mNext.getUid();
+ if (mAttributionSourceState.next != null) {
+ return mAttributionSourceState.next[0].uid;
}
return Process.INVALID_UID;
}
@@ -270,8 +233,8 @@
* @hide
*/
public @Nullable String getNextPackageName() {
- if (mNext != null) {
- return mNext.getPackageName();
+ if (mAttributionSourceState.next != null) {
+ return mAttributionSourceState.next[0].packageName;
}
return null;
}
@@ -283,8 +246,8 @@
* @hide
*/
public @Nullable String getNextAttributionTag() {
- if (mNext != null) {
- return mNext.getAttributionTag();
+ if (mAttributionSourceState.next != null) {
+ return mAttributionSourceState.next[0].attributionTag;
}
return null;
}
@@ -297,8 +260,9 @@
* @return Whether this is a trusted source.
*/
public boolean isTrusted(@NonNull Context context) {
- return mToken != null && context.getSystemService(PermissionManager.class)
- .isRegisteredAttributionSource(this);
+ return mAttributionSourceState.token != null
+ && context.getSystemService(PermissionManager.class)
+ .isRegisteredAttributionSource(this);
}
/**
@@ -310,71 +274,36 @@
@RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
@NonNull
public Set<String> getRenouncedPermissions() {
- return CollectionUtils.emptyIfNull(mRenouncedPermissions);
- }
-
- @DataClass.Suppress({"setUid", "setToken"})
- static class BaseBuilder {}
-
-
-
-
-
-
- // Code below generated by codegen v1.0.22.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/AttributionSource.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- /* package-private */ AttributionSource(
- int uid,
- @Nullable String packageName,
- @Nullable String attributionTag,
- @Nullable IBinder token,
- @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> renouncedPermissions,
- @Nullable AttributionSource next) {
- this.mUid = uid;
- this.mPackageName = packageName;
- this.mAttributionTag = attributionTag;
- this.mToken = token;
- this.mRenouncedPermissions = renouncedPermissions;
- com.android.internal.util.AnnotationValidations.validate(
- SystemApi.class, null, mRenouncedPermissions);
- com.android.internal.util.AnnotationValidations.validate(
- RequiresPermission.class, null, mRenouncedPermissions,
- "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
- this.mNext = next;
-
- // onConstructed(); // You can define this method to get a callback
+ if (mRenouncedPermissionsCached == null) {
+ if (mAttributionSourceState.renouncedPermissions != null) {
+ mRenouncedPermissionsCached = new ArraySet<>(
+ mAttributionSourceState.renouncedPermissions);
+ } else {
+ mRenouncedPermissionsCached = Collections.emptySet();
+ }
+ }
+ return mRenouncedPermissionsCached;
}
/**
* The UID that is accessing the permission protected data.
*/
public int getUid() {
- return mUid;
+ return mAttributionSourceState.uid;
}
/**
* The package that is accessing the permission protected data.
*/
public @Nullable String getPackageName() {
- return mPackageName;
+ return mAttributionSourceState.packageName;
}
/**
* The attribution tag of the app accessing the permission protected data.
*/
public @Nullable String getAttributionTag() {
- return mAttributionTag;
+ return mAttributionSourceState.attributionTag;
}
/**
@@ -383,113 +312,56 @@
* @hide
*/
public @Nullable IBinder getToken() {
- return mToken;
+ return mAttributionSourceState.token;
}
/**
* The next app to receive the permission protected data.
*/
public @Nullable AttributionSource getNext() {
- return mNext;
+ if (mNextCached == null && mAttributionSourceState.next != null) {
+ mNextCached = new AttributionSource(mAttributionSourceState.next[0]);
+ }
+ return mNextCached;
}
@Override
public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(AttributionSource other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
AttributionSource that = (AttributionSource) o;
- //noinspection PointlessBooleanExpression
- return true
- && mUid == that.mUid
- && Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mAttributionTag, that.mAttributionTag)
- && Objects.equals(mToken, that.mToken)
- && Objects.equals(mRenouncedPermissions, that.mRenouncedPermissions)
- && Objects.equals(mNext, that.mNext);
+ return mAttributionSourceState.uid == that.mAttributionSourceState.uid
+ && Objects.equals(mAttributionSourceState.packageName,
+ that.mAttributionSourceState.packageName)
+ && Objects.equals(mAttributionSourceState.attributionTag,
+ that.mAttributionSourceState.attributionTag)
+ && Objects.equals(mAttributionSourceState.token,
+ that.mAttributionSourceState.token)
+ && Arrays.equals(mAttributionSourceState.renouncedPermissions,
+ that.mAttributionSourceState.renouncedPermissions)
+ && Objects.equals(getNext(), that.getNext());
}
@Override
public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
int _hash = 1;
- _hash = 31 * _hash + mUid;
- _hash = 31 * _hash + Objects.hashCode(mPackageName);
- _hash = 31 * _hash + Objects.hashCode(mAttributionTag);
- _hash = 31 * _hash + Objects.hashCode(mToken);
- _hash = 31 * _hash + Objects.hashCode(mRenouncedPermissions);
- _hash = 31 * _hash + Objects.hashCode(mNext);
+ _hash = 31 * _hash + mAttributionSourceState.uid;
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.packageName);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.attributionTag);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.token);
+ _hash = 31 * _hash + Objects.hashCode(mAttributionSourceState.renouncedPermissions);
+ _hash = 31 * _hash + Objects.hashCode(getNext());
return _hash;
}
- static Parcelling<Set<String>> sParcellingForRenouncedPermissions =
- Parcelling.Cache.get(
- RenouncedPermissionsParcelling.class);
- static {
- if (sParcellingForRenouncedPermissions == null) {
- sParcellingForRenouncedPermissions = Parcelling.Cache.put(
- new RenouncedPermissionsParcelling());
- }
- }
-
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- byte flg = 0;
- if (mPackageName != null) flg |= 0x2;
- if (mAttributionTag != null) flg |= 0x4;
- if (mToken != null) flg |= 0x8;
- if (mRenouncedPermissions != null) flg |= 0x10;
- if (mNext != null) flg |= 0x20;
- dest.writeByte(flg);
- dest.writeInt(mUid);
- if (mPackageName != null) dest.writeString(mPackageName);
- if (mAttributionTag != null) dest.writeString(mAttributionTag);
- if (mToken != null) dest.writeStrongBinder(mToken);
- sParcellingForRenouncedPermissions.parcel(mRenouncedPermissions, dest, flags);
- if (mNext != null) dest.writeTypedObject(mNext, flags);
+ mAttributionSourceState.writeToParcel(dest, flags);
}
@Override
public int describeContents() { return 0; }
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- /* package-private */ AttributionSource(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- byte flg = in.readByte();
- int uid = in.readInt();
- String packageName = (flg & 0x2) == 0 ? null : in.readString();
- String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
- IBinder token = (flg & 0x8) == 0 ? null : in.readStrongBinder();
- Set<String> renouncedPermissions = sParcellingForRenouncedPermissions.unparcel(in);
- AttributionSource next = (flg & 0x20) == 0 ? null : (AttributionSource) in.readTypedObject(AttributionSource.CREATOR);
-
- this.mUid = uid;
- this.mPackageName = packageName;
- this.mAttributionTag = attributionTag;
- this.mToken = token;
- this.mRenouncedPermissions = renouncedPermissions;
- com.android.internal.util.AnnotationValidations.validate(
- SystemApi.class, null, mRenouncedPermissions);
- com.android.internal.util.AnnotationValidations.validate(
- RequiresPermission.class, null, mRenouncedPermissions,
- "value", android.Manifest.permission.RENOUNCE_PERMISSIONS);
- this.mNext = next;
-
- // onConstructed(); // You can define this method to get a callback
- }
-
public static final @NonNull Parcelable.Creator<AttributionSource> CREATOR
= new Parcelable.Creator<AttributionSource>() {
@Override
@@ -506,15 +378,9 @@
/**
* A builder for {@link AttributionSource}
*/
- @SuppressWarnings("WeakerAccess")
- public static final class Builder extends BaseBuilder {
-
- private int mUid;
- private @Nullable String mPackageName;
- private @Nullable String mAttributionTag;
- private @Nullable IBinder mToken;
- private @Nullable Set<String> mRenouncedPermissions;
- private @Nullable AttributionSource mNext;
+ public static final class Builder {
+ private @NonNull final AttributionSourceState mAttributionSourceState =
+ new AttributionSourceState();
private long mBuilderFieldsSet = 0L;
@@ -524,9 +390,8 @@
* @param uid
* The UID that is accessing the permission protected data.
*/
- public Builder(
- int uid) {
- mUid = uid;
+ public Builder(int uid) {
+ mAttributionSourceState.uid = uid;
}
/**
@@ -535,7 +400,7 @@
public @NonNull Builder setPackageName(@Nullable String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
- mPackageName = value;
+ mAttributionSourceState.packageName = value;
return this;
}
@@ -545,7 +410,7 @@
public @NonNull Builder setAttributionTag(@Nullable String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
- mAttributionTag = value;
+ mAttributionSourceState.attributionTag = value;
return this;
}
@@ -578,7 +443,8 @@
public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
- mRenouncedPermissions = value;
+ mAttributionSourceState.renouncedPermissions = (value != null)
+ ? value.toArray(new String[0]) : null;
return this;
}
@@ -588,7 +454,8 @@
public @NonNull Builder setNext(@Nullable AttributionSource value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
- mNext = value;
+ mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
+ {value.mAttributionSourceState} : null;
return this;
}
@@ -598,28 +465,21 @@
mBuilderFieldsSet |= 0x40; // Mark builder used
if ((mBuilderFieldsSet & 0x2) == 0) {
- mPackageName = null;
+ mAttributionSourceState.packageName = null;
}
if ((mBuilderFieldsSet & 0x4) == 0) {
- mAttributionTag = null;
+ mAttributionSourceState.attributionTag = null;
}
if ((mBuilderFieldsSet & 0x8) == 0) {
- mToken = null;
+ mAttributionSourceState.token = null;
}
if ((mBuilderFieldsSet & 0x10) == 0) {
- mRenouncedPermissions = null;
+ mAttributionSourceState.renouncedPermissions = null;
}
if ((mBuilderFieldsSet & 0x20) == 0) {
- mNext = null;
+ mAttributionSourceState.next = null;
}
- AttributionSource o = new AttributionSource(
- mUid,
- mPackageName,
- mAttributionTag,
- mToken,
- mRenouncedPermissions,
- mNext);
- return o;
+ return new AttributionSource(mAttributionSourceState);
}
private void checkNotUsed() {
@@ -629,9 +489,4 @@
}
}
}
-
-
- //@formatter:on
- // End of generated code
-
}
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 5089f30..66e0883 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -16,21 +16,19 @@
package android.content;
-import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
import android.os.Binder;
+import android.os.IBinder;
import android.os.Process;
-import android.util.Slog;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.permission.IPermissionChecker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
/**
* This class provides permission check APIs that verify both the
@@ -72,34 +70,44 @@
* @hide
*/
public final class PermissionChecker {
- private static final String LOG_TAG = PermissionChecker.class.getName();
+ /**
+ * The permission is granted.
+ *
+ * @hide
+ */
+ public static final int PERMISSION_GRANTED = IPermissionChecker.PERMISSION_GRANTED;
- private static final String PLATFORM_PACKAGE_NAME = "android";
-
- /** The permission is granted. */
- public static final int PERMISSION_GRANTED = AppOpsManager.MODE_ALLOWED;
-
- /** Only for runtime permissions, its returned when the runtime permission
- * is granted, but the corresponding app op is denied. */
- public static final int PERMISSION_SOFT_DENIED = AppOpsManager.MODE_IGNORED;
-
- /** Returned when:
+ /**
+ * The permission is denied. Applicable only to runtime and app op permissions.
+ *
+ * <p>Returned when:
* <ul>
- * <li>For non app op permissions, returned when the permission is denied.</li>
- * <li>For app op permissions, returned when the app op is denied or app op is
- * {@link AppOpsManager#MODE_DEFAULT} and permission is denied.</li>
+ * <li>the runtime permission is granted, but the corresponding app op is denied
+ * for runtime permissions.</li>
+ * <li>the app ops is ignored for app op permissions.</li>
* </ul>
*
+ * @hide
*/
- public static final int PERMISSION_HARD_DENIED = AppOpsManager.MODE_ERRORED;
+ public static final int PERMISSION_SOFT_DENIED = IPermissionChecker.PERMISSION_SOFT_DENIED;
+
+ /**
+ * The permission is denied.
+ *
+ * <p>Returned when:
+ * <ul>
+ * <li>the permission is denied for non app op permissions.</li>
+ * <li>the app op is denied or app op is {@link AppOpsManager#MODE_DEFAULT}
+ * and permission is denied.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ public static final int PERMISSION_HARD_DENIED = IPermissionChecker.PERMISSION_HARD_DENIED;
/** Constant when the PID for which we check permissions is unknown. */
public static final int PID_UNKNOWN = -1;
- // Cache for platform defined runtime permissions to avoid multi lookup (name -> info)
- private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
- = new ConcurrentHashMap<>();
-
/** @hide */
@IntDef({PERMISSION_GRANTED,
PERMISSION_SOFT_DENIED,
@@ -107,6 +115,8 @@
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionResult {}
+ private static volatile IPermissionChecker sService;
+
private PermissionChecker() {
/* do nothing */
}
@@ -232,7 +242,7 @@
public static int checkPermissionForDataDeliveryFromDataSource(@NonNull Context context,
@NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ return checkPermissionForDataDeliveryCommon(context, permission, attributionSource,
message, false /*startDataDelivery*/, /*fromDatasource*/ true);
}
@@ -307,21 +317,23 @@
public static int checkPermissionForDataDelivery(@NonNull Context context,
@NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean startDataDelivery) {
- return checkPermissionForDataDeliveryCommon(context, permission, pid, attributionSource,
+ return checkPermissionForDataDeliveryCommon(context, permission, attributionSource,
message, startDataDelivery, /*fromDatasource*/ false);
}
private static int checkPermissionForDataDeliveryCommon(@NonNull Context context,
- @NonNull String permission, int pid, @NonNull AttributionSource attributionSource,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message, boolean startDataDelivery, boolean fromDatasource) {
// If the check failed in the middle of the chain, finish any started op.
- final int result = checkPermissionCommon(context, permission, attributionSource,
- message, true /*forDataDelivery*/, startDataDelivery, fromDatasource);
- if (startDataDelivery && result != PERMISSION_GRANTED) {
- finishDataDelivery(context, AppOpsManager.permissionToOp(permission),
- attributionSource);
+ try {
+ final int result = getPermissionCheckerService().checkPermission(permission,
+ attributionSource.asState(), message, true /*forDataDelivery*/,
+ startDataDelivery, fromDatasource);
+ return result;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
- return result;
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -356,9 +368,14 @@
public static int checkPermissionAndStartDataDelivery(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkPermissionCommon(context, permission, attributionSource,
- message, true /*forDataDelivery*/, /*startDataDelivery*/ true,
- /*fromDatasource*/ false);
+ try {
+ return getPermissionCheckerService().checkPermission(permission,
+ attributionSource.asState(), message, true /*forDataDelivery*/,
+ /*startDataDelivery*/ true, /*fromDatasource*/ false);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -390,13 +407,14 @@
public static int startOpForDataDelivery(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- final int result = checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
- message, true /*forDataDelivery*/, true /*startDataDelivery*/);
- // It is important to finish any started op if some step in the attribution chain failed.
- if (result != PERMISSION_GRANTED) {
- finishDataDelivery(context, opName, attributionSource);
+ try {
+ return getPermissionCheckerService().checkOp(
+ AppOpsManager.strOpToOp(opName), attributionSource.asState(), message,
+ true /*forDataDelivery*/, true /*startDataDelivery*/);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
- return result;
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -412,15 +430,10 @@
*/
public static void finishDataDelivery(@NonNull Context context, @NonNull String op,
@NonNull AttributionSource attributionSource) {
- if (op == null || attributionSource.getPackageName() == null) {
- return;
- }
-
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- appOpsManager.finishProxyOp(op, attributionSource);
-
- if (attributionSource.getNext() != null) {
- finishDataDelivery(context, op, attributionSource.getNext());
+ try {
+ getPermissionCheckerService().finishDataDelivery(op, attributionSource.asState());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
@@ -456,8 +469,14 @@
public static int checkOpForPreflight(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
- message, false /*forDataDelivery*/, false /*startDataDelivery*/);
+ try {
+ return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName),
+ attributionSource.asState(), message, false /*forDataDelivery*/,
+ false /*startDataDelivery*/);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -489,8 +508,14 @@
public static int checkOpForDataDelivery(@NonNull Context context,
@NonNull String opName, @NonNull AttributionSource attributionSource,
@Nullable String message) {
- return checkOp(context, AppOpsManager.strOpToOp(opName), attributionSource,
- message, true /*forDataDelivery*/, false /*startDataDelivery*/);
+ try {
+ return getPermissionCheckerService().checkOp(AppOpsManager.strOpToOp(opName),
+ attributionSource.asState(), message, true /*forDataDelivery*/,
+ false /*startDataDelivery*/);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -561,9 +586,14 @@
@PermissionResult
public static int checkPermissionForPreflight(@NonNull Context context,
@NonNull String permission, @NonNull AttributionSource attributionSource) {
- return checkPermissionCommon(context, permission, attributionSource,
- null /*message*/, false /*forDataDelivery*/, /*startDataDelivery*/ false,
- /*fromDatasource*/ false);
+ try {
+ return getPermissionCheckerService().checkPermission(permission,
+ attributionSource.asState(), null /*message*/, false /*forDataDelivery*/,
+ /*startDataDelivery*/ false, /*fromDatasource*/ false);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return PERMISSION_HARD_DENIED;
}
/**
@@ -798,356 +828,12 @@
Binder.getCallingUid(), packageName);
}
- @PermissionResult
- private static int checkPermissionCommon(@NonNull Context context, @NonNull String permission,
- @NonNull AttributionSource attributionSource,
- @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
- boolean fromDatasource) {
- PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
-
- if (permissionInfo == null) {
- try {
- permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
- if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) {
- // Double addition due to concurrency is fine - the backing store is concurrent.
- sPlatformPermissions.put(permission, permissionInfo);
- }
- } catch (PackageManager.NameNotFoundException ignored) {
- return PERMISSION_HARD_DENIED;
- }
+ private static @NonNull IPermissionChecker getPermissionCheckerService() {
+ // Race is fine, we may end up looking up the same instance twice, no big deal.
+ if (sService == null) {
+ final IBinder service = ServiceManager.getService("permission_checker");
+ sService = IPermissionChecker.Stub.asInterface(service);
}
-
- if (permissionInfo.isAppOp()) {
- return checkAppOpPermission(context, permission, attributionSource, message,
- forDataDelivery, fromDatasource);
- }
- if (permissionInfo.isRuntime()) {
- return checkRuntimePermission(context, permission, attributionSource, message,
- forDataDelivery, startDataDelivery, fromDatasource);
- }
-
- if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
- attributionSource.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
-
- if (attributionSource.getNext() != null) {
- return checkPermissionCommon(context, permission,
- attributionSource.getNext(), message, forDataDelivery,
- startDataDelivery, /*fromDatasource*/ false);
- }
-
- return PERMISSION_GRANTED;
- }
-
- @PermissionResult
- private static int checkAppOpPermission(@NonNull Context context, @NonNull String permission,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean fromDatasource) {
- final int op = AppOpsManager.permissionToOpCode(permission);
- if (op < 0) {
- Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!");
- return PERMISSION_HARD_DENIED;
- }
-
- AttributionSource current = attributionSource;
- AttributionSource next = null;
-
- while (true) {
- final boolean skipCurrentChecks = (fromDatasource || next != null);
-
- next = current.getNext();
-
- // If the call is from a datasource we need to vet only the chain before it. This
- // way we can avoid the datasource creating an attribution context for every call.
- if (!(fromDatasource && current == attributionSource)
- && next != null && !current.isTrusted(context)) {
- return PERMISSION_HARD_DENIED;
- }
-
- // The access is for oneself if this is the single receiver of data
- // after the data source or if this is the single attribution source
- // in the chain if not from a datasource.
- final boolean singleReceiverFromDatasource = (fromDatasource
- && current == attributionSource && next != null && next.getNext() == null);
- final boolean selfAccess = singleReceiverFromDatasource || next == null;
-
- final int opMode = performOpTransaction(context, op, current, message,
- forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks,
- selfAccess, singleReceiverFromDatasource);
-
- switch (opMode) {
- case AppOpsManager.MODE_IGNORED:
- case AppOpsManager.MODE_ERRORED: {
- return PERMISSION_HARD_DENIED;
- }
- case AppOpsManager.MODE_DEFAULT: {
- if (!skipCurrentChecks && !checkPermission(context, permission,
- attributionSource.getUid(), attributionSource
- .getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
- if (next != null && !checkPermission(context, permission,
- next.getUid(), next.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
- }
- }
-
- if (next == null || next.getNext() == null) {
- return PERMISSION_GRANTED;
- }
-
- current = next;
- }
- }
-
- private static int checkRuntimePermission(@NonNull Context context, @NonNull String permission,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
- // Now let's check the identity chain...
- final int op = AppOpsManager.permissionToOpCode(permission);
-
- AttributionSource current = attributionSource;
- AttributionSource next = null;
-
- while (true) {
- final boolean skipCurrentChecks = (fromDatasource || next != null);
- next = current.getNext();
-
- // If the call is from a datasource we need to vet only the chain before it. This
- // way we can avoid the datasource creating an attribution context for every call.
- if (!(fromDatasource && current == attributionSource)
- && next != null && !current.isTrusted(context)) {
- return PERMISSION_HARD_DENIED;
- }
-
- // If we already checked the permission for this one, skip the work
- if (!skipCurrentChecks && !checkPermission(context, permission,
- current.getUid(), current.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
-
- if (next != null && !checkPermission(context, permission,
- next.getUid(), next.getRenouncedPermissions())) {
- return PERMISSION_HARD_DENIED;
- }
-
- if (op < 0) {
- // Bg location is one-off runtime modifier permission and has no app op
- if (sPlatformPermissions.contains(permission)
- && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) {
- Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
- + " with no app op defined!");
- }
- if (next == null) {
- return PERMISSION_GRANTED;
- }
- current = next;
- continue;
- }
-
- // The access is for oneself if this is the single receiver of data
- // after the data source or if this is the single attribution source
- // in the chain if not from a datasource.
- final boolean singleReceiverFromDatasource = (fromDatasource
- && current == attributionSource && next != null && next.getNext() == null);
- final boolean selfAccess = singleReceiverFromDatasource || next == null;
-
- final int opMode = performOpTransaction(context, op, current, message,
- forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
- singleReceiverFromDatasource);
-
- switch (opMode) {
- case AppOpsManager.MODE_ERRORED: {
- return PERMISSION_HARD_DENIED;
- }
- case AppOpsManager.MODE_IGNORED: {
- return PERMISSION_SOFT_DENIED;
- }
- }
-
- if (next == null || next.getNext() == null) {
- return PERMISSION_GRANTED;
- }
-
- current = next;
- }
- }
-
- private static boolean checkPermission(@NonNull Context context, @NonNull String permission,
- int uid, @NonNull Set<String> renouncedPermissions) {
- final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
- uid) == PackageManager.PERMISSION_GRANTED;
- if (permissionGranted && renouncedPermissions.contains(permission)
- && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
- /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- return permissionGranted;
- }
-
- private static int checkOp(@NonNull Context context, @NonNull int op,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery) {
- if (op < 0 || attributionSource.getPackageName() == null) {
- return PERMISSION_HARD_DENIED;
- }
-
- AttributionSource current = attributionSource;
- AttributionSource next = null;
-
- while (true) {
- final boolean skipCurrentChecks = (next != null);
- next = current.getNext();
-
- // If the call is from a datasource we need to vet only the chain before it. This
- // way we can avoid the datasource creating an attribution context for every call.
- if (next != null && !current.isTrusted(context)) {
- return PERMISSION_HARD_DENIED;
- }
-
- // The access is for oneself if this is the single attribution source in the chain.
- final boolean selfAccess = (next == null);
-
- final int opMode = performOpTransaction(context, op, current, message,
- forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
- /*fromDatasource*/ false);
-
- switch (opMode) {
- case AppOpsManager.MODE_ERRORED: {
- return PERMISSION_HARD_DENIED;
- }
- case AppOpsManager.MODE_IGNORED: {
- return PERMISSION_SOFT_DENIED;
- }
- }
-
- if (next == null || next.getNext() == null) {
- return PERMISSION_GRANTED;
- }
-
- current = next;
- }
- }
-
- private static int performOpTransaction(@NonNull Context context, int op,
- @NonNull AttributionSource attributionSource, @Nullable String message,
- boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
- boolean selfAccess, boolean singleReceiverFromDatasource) {
- // We cannot perform app ops transactions without a package name. In all relevant
- // places we pass the package name but just in case there is a bug somewhere we
- // do a best effort to resolve the package from the UID (pick first without a loss
- // of generality - they are in the same security sandbox).
- final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final AttributionSource accessorSource = (!singleReceiverFromDatasource)
- ? attributionSource : attributionSource.getNext();
- if (!forDataDelivery) {
- final String resolvedAccessorPackageName = resolvePackageName(context, accessorSource);
- if (resolvedAccessorPackageName == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
- accessorSource.getUid(), resolvedAccessorPackageName);
- final AttributionSource next = accessorSource.getNext();
- if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) {
- final String resolvedNextPackageName = resolvePackageName(context, next);
- if (resolvedNextPackageName == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
- resolvedNextPackageName);
- }
- return opMode;
- } else if (startDataDelivery) {
- final AttributionSource resolvedAttributionSource = resolveAttributionSource(
- context, accessorSource);
- if (resolvedAttributionSource.getPackageName() == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- if (selfAccess) {
- // If the datasource is not in a trusted platform component then in would not
- // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
- // an app is exposing runtime permission protected data but cannot blame others
- // in a trusted way which would not properly show in permission usage UIs.
- // As a fallback we note a proxy op that blames the app and the datasource.
- try {
- return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
- resolvedAttributionSource.getPackageName(),
- /*startIfModeDefault*/ false,
- resolvedAttributionSource.getAttributionTag(),
- message);
- } catch (SecurityException e) {
- Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
- + " platform defined runtime permission "
- + AppOpsManager.opToPermission(op) + " while not having "
- + Manifest.permission.UPDATE_APP_OPS_STATS);
- return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
- skipProxyOperation);
- }
- } else {
- return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
- skipProxyOperation);
- }
- } else {
- final AttributionSource resolvedAttributionSource = resolveAttributionSource(
- context, accessorSource);
- if (resolvedAttributionSource.getPackageName() == null) {
- return AppOpsManager.MODE_ERRORED;
- }
- if (selfAccess) {
- // If the datasource is not in a trusted platform component then in would not
- // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
- // an app is exposing runtime permission protected data but cannot blame others
- // in a trusted way which would not properly show in permission usage UIs.
- // As a fallback we note a proxy op that blames the app and the datasource.
- try {
- return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
- resolvedAttributionSource.getPackageName(),
- resolvedAttributionSource.getAttributionTag(),
- message);
- } catch (SecurityException e) {
- Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
- + " platform defined runtime permission "
- + AppOpsManager.opToPermission(op) + " while not having "
- + Manifest.permission.UPDATE_APP_OPS_STATS);
- return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
- skipProxyOperation);
- }
- } else {
- return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
- skipProxyOperation);
- }
- }
- }
-
- private static @Nullable String resolvePackageName(@NonNull Context context,
- @NonNull AttributionSource attributionSource) {
- if (attributionSource.getPackageName() != null) {
- return attributionSource.getPackageName();
- }
- final String[] packageNames = context.getPackageManager().getPackagesForUid(
- attributionSource.getUid());
- if (packageNames != null) {
- // This is best effort if the caller doesn't pass a package. The security
- // sandbox is UID, therefore we pick an arbitrary package.
- return packageNames[0];
- }
- // Last resort to handle special UIDs like root, etc.
- return AppOpsManager.resolvePackageName(attributionSource.getUid(),
- attributionSource.getPackageName());
- }
-
- private static @NonNull AttributionSource resolveAttributionSource(
- @NonNull Context context, @NonNull AttributionSource attributionSource) {
- if (attributionSource.getPackageName() != null) {
- return attributionSource;
- }
- return new AttributionSource(attributionSource.getUid(),
- resolvePackageName(context, attributionSource),
- attributionSource.getAttributionTag(),
- attributionSource.getToken(),
- attributionSource.getRenouncedPermissions(),
- attributionSource.getNext());
+ return sService;
}
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 725576f..22d75ef 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -3069,13 +3069,6 @@
/**
* @hide
*/
- public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
- sCompatibilityModeEnabled = compatibilityModeEnabled;
- }
-
- /**
- * @hide
- */
public static void readConfigUseRoundIcon(Resources r) {
if (r != null) {
sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 51d196d..17116e2 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -223,14 +223,6 @@
public static final long BLOCK_FLAG_SLIPPERY = android.os.IInputConstants.BLOCK_FLAG_SLIPPERY;
/**
- * Check whether apps are using MotionEvent.getRawX/Y. This is implementation-specific, and
- * thus undefined for most 3p app usages.
- * @hide
- */
- @ChangeId
- public static final long APP_USES_RAW_INPUT_COORDS = 179274888L;
-
- /**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* @hide
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index d026e95..2a9b703 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -560,9 +560,6 @@
}
}
- final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
- AppOpsManager.pauseNotedAppOpsCollection();
-
if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
flags |= FLAG_COLLECT_NOTED_APP_OPS;
}
@@ -571,8 +568,6 @@
boolean replyOwnsNative = (reply == null) ? false : reply.ownsNativeParcelObject();
return transactNative(code, data, reply, replyOwnsNative, flags);
} finally {
- AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
-
if (transactListener != null) {
transactListener.onTransactEnded(session);
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index b2c1e7a..820f531 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -837,8 +837,8 @@
new MediaSize("JPN_YOU4", "android",
R.string.mediasize_japanese_you4, 4134, 9252);
/** Japanese Photo L media size: 89mm x 127mm (3.5 x 5") */
- public static final @NonNull MediaSize OE_PHOTO_L =
- new MediaSize("OE_PHOTO_L", "android",
+ public static final @NonNull MediaSize JPN_OE_PHOTO_L =
+ new MediaSize("JPN_OE_PHOTO_L", "android",
R.string.mediasize_japanese_l, 3500, 5000);
private final @NonNull String mId;
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 50f63f8..c75e238 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,7 +16,7 @@
package android.util;
-import libcore.internal.StringPool;
+import com.android.internal.util.StringPool;
import java.io.Closeable;
import java.io.EOFException;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 31ca8e1..6801c27 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -16,7 +16,6 @@
package android.view;
-import static android.hardware.input.InputManager.APP_USES_RAW_INPUT_COORDS;
import static android.view.Display.DEFAULT_DISPLAY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -24,7 +23,6 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
-import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Matrix;
import android.os.Build;
@@ -2674,7 +2672,6 @@
* @see #AXIS_X
*/
public final float getRawX() {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
@@ -2688,7 +2685,6 @@
* @see #AXIS_Y
*/
public final float getRawY() {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
}
@@ -2705,7 +2701,6 @@
* @see #AXIS_X
*/
public float getRawX(int pointerIndex) {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
}
@@ -2722,7 +2717,6 @@
* @see #AXIS_Y
*/
public float getRawY(int pointerIndex) {
- Compatibility.reportUnconditionalChange(APP_USES_RAW_INPUT_COORDS);
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
}
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index 6b49569..ad123c1 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,10 +16,10 @@
package android.webkit;
-import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.UptimeMillisLong;
import android.app.ActivityThread;
import android.app.Application;
import android.app.ResourcesManager;
@@ -227,7 +227,7 @@
* WebViewChromiumFactoryProvider#create method was invoked.
*/
@NonNull
- @ElapsedRealtimeLong
+ @UptimeMillisLong
public long[] getTimestamps() {
return WebViewFactory.getTimestamps();
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 5fc5b29..9a38512 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -273,7 +273,7 @@
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
- sTimestamps[WEBVIEW_LOAD_START] = SystemClock.elapsedRealtime();
+ sTimestamps[WEBVIEW_LOAD_START] = SystemClock.uptimeMillis();
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
|| uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
@@ -413,7 +413,7 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
- sTimestamps[CREATE_CONTEXT_START] = SystemClock.elapsedRealtime();
+ sTimestamps[CREATE_CONTEXT_START] = SystemClock.uptimeMillis();
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
@@ -422,7 +422,7 @@
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
- sTimestamps[CREATE_CONTEXT_END] = SystemClock.elapsedRealtime();
+ sTimestamps[CREATE_CONTEXT_END] = SystemClock.uptimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
@@ -448,26 +448,26 @@
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
- sTimestamps[ADD_ASSETS_START] = SystemClock.elapsedRealtime();
+ sTimestamps[ADD_ASSETS_START] = SystemClock.uptimeMillis();
for (String newAssetPath : webViewContext.getApplicationInfo().getAllApkPaths()) {
initialApplication.getAssets().addAssetPathAsSharedLibrary(newAssetPath);
}
sTimestamps[ADD_ASSETS_END] = sTimestamps[GET_CLASS_LOADER_START] =
- SystemClock.elapsedRealtime();
+ SystemClock.uptimeMillis();
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
sTimestamps[GET_CLASS_LOADER_END] = sTimestamps[NATIVE_LOAD_START] =
- SystemClock.elapsedRealtime();
+ SystemClock.uptimeMillis();
WebViewLibraryLoader.loadNativeLibrary(clazzLoader,
getWebViewLibrary(sPackageInfo.applicationInfo));
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
sTimestamps[NATIVE_LOAD_END] = sTimestamps[PROVIDER_CLASS_FOR_NAME_START] =
- SystemClock.elapsedRealtime();
+ SystemClock.uptimeMillis();
try {
return getWebViewProviderClass(clazzLoader);
} finally {
- sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = SystemClock.elapsedRealtime();
+ sTimestamps[PROVIDER_CLASS_FOR_NAME_END] = SystemClock.uptimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (ClassNotFoundException e) {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0dbdb8f..ee7818c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -385,6 +385,11 @@
*/
private int mViewId = View.NO_ID;
+ /**
+ * Id used to uniquely identify a {@link RemoteViews} instance coming from a given provider.
+ */
+ private long mProviderInstanceId = -1;
+
/** Class cookies of the Parcel this instance was read from. */
private Map<Class, Object> mClassCookies;
@@ -3646,6 +3651,7 @@
mApplyFlags = src.mApplyFlags;
mClassCookies = src.mClassCookies;
mIdealSize = src.mIdealSize;
+ mProviderInstanceId = src.mProviderInstanceId;
if (src.hasLandscapeAndPortraitLayouts()) {
mLandscape = new RemoteViews(src.mLandscape);
@@ -3744,6 +3750,7 @@
mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
}
mApplyFlags = parcel.readInt();
+ mProviderInstanceId = parcel.readLong();
}
private void readActionsFromParcel(Parcel parcel, int depth) {
@@ -6021,6 +6028,7 @@
mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
}
dest.writeInt(mApplyFlags);
+ dest.writeLong(mProviderInstanceId);
}
private void writeActionsToParcel(Parcel parcel) {
@@ -6711,4 +6719,85 @@
public @IdRes int getViewId() {
return mViewId;
}
+
+ /**
+ * Set the provider instance ID.
+ *
+ * This should only be used by {@link com.android.server.appwidget.AppWidgetService}.
+ * @hide
+ */
+ public void setProviderInstanceId(long id) {
+ mProviderInstanceId = id;
+ }
+
+ /**
+ * Get the provider instance id.
+ *
+ * This should uniquely identifies {@link RemoteViews} coming from a given App Widget
+ * Provider. This changes each time the App Widget provider update the {@link RemoteViews} of
+ * its widget. Returns -1 if the {@link RemoteViews} doesn't come from an App Widget provider.
+ * @hide
+ */
+ public long getProviderInstanceId() {
+ return mProviderInstanceId;
+ }
+
+ /**
+ * Identify the child of this {@link RemoteViews}, or 0 if this is not a child.
+ *
+ * The returned value is always a small integer, currently between 0 and 17.
+ */
+ private int getChildId(@NonNull RemoteViews child) {
+ if (child == this) {
+ return 0;
+ }
+ if (hasSizedRemoteViews()) {
+ for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+ if (mSizedRemoteViews.get(i) == child) {
+ return i + 1;
+ }
+ }
+ }
+ if (hasLandscapeAndPortraitLayouts()) {
+ if (mLandscape == child) {
+ return 1;
+ } else if (mPortrait == child) {
+ return 2;
+ }
+ }
+ // This is not a child of this RemoteViews.
+ return 0;
+ }
+
+ /**
+ * Identify uniquely this RemoteViews, or returns -1 if not possible.
+ *
+ * @param parent If the {@link RemoteViews} is not a root {@link RemoteViews}, this should be
+ * the parent that contains it.
+ *
+ * @hide
+ */
+ public long computeUniqueId(@Nullable RemoteViews parent) {
+ if (mIsRoot) {
+ long viewId = getProviderInstanceId();
+ if (viewId != -1) {
+ viewId <<= 8;
+ }
+ return viewId;
+ }
+ if (parent == null) {
+ return -1;
+ }
+ long viewId = parent.getProviderInstanceId();
+ if (viewId == -1) {
+ return -1;
+ }
+ int childId = parent.getChildId(this);
+ if (childId == -1) {
+ return -1;
+ }
+ viewId <<= 8;
+ viewId |= childId;
+ return viewId;
+ }
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 9749a68..6b33428 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -86,8 +86,8 @@
// Default height for the default loading view, in case we cannot get inflate the first view
private static final int DEFAULT_LOADING_VIEW_HEIGHT = 50;
- // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
- // structures;
+ // We cache the FixedSizeRemoteViewsCaches across orientation and re-inflation due to color
+ // palette changes. These are the related data structures:
private static final HashMap<RemoteViewsCacheKey, FixedSizeRemoteViewsCache>
sCachedRemoteViewsCaches = new HashMap<>();
private static final HashMap<RemoteViewsCacheKey, Runnable>
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 933760c..8debe5d 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -70,6 +70,7 @@
private static final boolean DEBUG = false;
private boolean mNotCopyable;
+ private boolean mRevealAnimationSupported = true;
private int mInitBackgroundColor;
private int mInitIconBackgroundColor;
private View mIconView;
@@ -264,6 +265,25 @@
}
/**
+ * If set to true, indicates to the system that this view can be dismissed by playing the
+ * Reveal animation.
+ * <p>
+ * If the exit animation is handled by the client, the animation won't be played anyway.
+ * @hide
+ */
+ public void setRevealAnimationSupported(boolean support) {
+ mRevealAnimationSupported = support;
+ }
+
+ /**
+ * Whether this view support reveal animation.
+ * @hide
+ */
+ public boolean isRevealAnimationSupported() {
+ return mRevealAnimationSupported;
+ }
+
+ /**
* Returns the duration of the icon animation if icon is animatable.
*
* @see android.R.attr#windowSplashScreenAnimatedIcon
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 2b4e09d..83def0c 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -80,6 +80,8 @@
"com.android.server.accessibility.MagnificationController";
public static final ComponentName MAGNIFICATION_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "Magnification");
+ public static final ComponentName ONE_HANDED_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "OneHandedMode");
public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
@@ -117,7 +119,7 @@
public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
getFrameworkShortcutFeaturesMap() {
if (sFrameworkShortcutFeaturesMap == null) {
- Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(2);
+ Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
@@ -128,6 +130,11 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
"1" /* Value to enable */, "0" /* Value to disable */,
R.string.color_correction_feature_name));
+ featuresMap.put(ONE_HANDED_COMPONENT_NAME,
+ new ToggleableFrameworkFeatureInfo(
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
+ "1" /* Value to enable */, "0" /* Value to disable */,
+ R.string.one_handed_mode_feature_name));
featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME,
new ToggleableFrameworkFeatureInfo(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index f2d91ba..4a20ad0 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -21,6 +21,7 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType;
import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained;
@@ -230,6 +231,16 @@
context.getDrawable(R.drawable.ic_accessibility_color_inversion),
Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ final ToggleAllowListingFeatureTarget oneHandedMode =
+ new ToggleAllowListingFeatureTarget(context,
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ ONE_HANDED_COMPONENT_NAME.flattenToString()),
+ ONE_HANDED_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.one_handed_mode_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_one_handed),
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED);
+
final ToggleAllowListingFeatureTarget reduceBrightColors =
new ToggleAllowListingFeatureTarget(context,
shortcutType,
@@ -243,6 +254,7 @@
targets.add(magnification);
targets.add(daltonizer);
targets.add(colorInversion);
+ targets.add(oneHandedMode);
targets.add(reduceBrightColors);
return targets;
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index 2785c21..bce0d60 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -93,7 +93,6 @@
STATE_LABELS[STATE_BOUND_TOP_OR_FGS] = "Bnd TopFgs";
STATE_LABELS[STATE_FGS] = " Fgs";
STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
- STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg";
STATE_LABELS[STATE_BACKUP] = " Backup";
STATE_LABELS[STATE_SERVICE] = " Service";
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 4bced27..d259b7f 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -1550,6 +1550,10 @@
final int key = mDurations.getKeyAt(i);
final int type = SparseMappingTable.getIdFromKey(key);
final int aggregatedType = DumpUtils.aggregateCurrentProcessState(type);
+ if ((type % STATE_COUNT) == STATE_SERVICE_RESTARTING) {
+ // Skip restarting service state -- that is not actually a running process.
+ continue;
+ }
long time = mDurations.getValue(key);
if (mCurCombinedState == type) {
@@ -1563,7 +1567,9 @@
durationByState.put(aggregatedType, time);
}
}
- if (!didCurState && mCurCombinedState != STATE_NOTHING) {
+ if (!didCurState && mCurCombinedState != STATE_NOTHING
+ && (mCurCombinedState % STATE_COUNT) != STATE_SERVICE_RESTARTING) {
+ // Skip restarting service state -- that is not actually a running process.
final int aggregatedType = DumpUtils.aggregateCurrentProcessState(mCurCombinedState);
int index = durationByState.indexOfKey(aggregatedType);
if (index >= 0) {
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index 36bc229..182dba7 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -39,14 +39,14 @@
* Changes forced to be enabled.
*/
public Set<Long> enabledChanges() {
- return mChangeConfig.forceEnabledSet();
+ return mChangeConfig.getEnabledSet();
}
/**
* Changes forced to be disabled.
*/
public Set<Long> disabledChanges() {
- return mChangeConfig.forceDisabledSet();
+ return mChangeConfig.getDisabledSet();
}
/**
@@ -84,8 +84,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- long[] enabled = mChangeConfig.forceEnabledChangesArray();
- long[] disabled = mChangeConfig.forceDisabledChangesArray();
+ long[] enabled = mChangeConfig.getEnabledChangesArray();
+ long[] disabled = mChangeConfig.getDisabledChangesArray();
dest.writeLongArray(enabled);
dest.writeLongArray(disabled);
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
index 0c93f7f..8ca6241 100644
--- a/core/java/com/android/internal/os/BinderDeathDispatcher.java
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -24,12 +24,11 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.PrintWriter;
-
/**
* Multiplexes multiple binder death recipients on the same binder objects, so that at the native
* level, we only need to keep track of one death recipient reference. This will help reduce the
@@ -63,6 +62,10 @@
@Override
public void binderDied() {
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
final ArraySet<DeathRecipient> copy;
synchronized (mLock) {
copy = mRecipients;
@@ -77,7 +80,7 @@
// Let's call it without holding the lock.
final int size = copy.size();
for (int i = 0; i < size; i++) {
- copy.valueAt(i).binderDied();
+ copy.valueAt(i).binderDied(who);
}
}
}
@@ -124,13 +127,12 @@
}
}
- public void dump(PrintWriter pw, String indent) {
+ /** Dump stats useful for debugging. Can be used from dump() methods of client services. */
+ public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
- pw.print(indent);
pw.print("# of watched binders: ");
pw.println(mTargets.size());
- pw.print(indent);
pw.print("# of death recipients: ");
int n = 0;
for (RecipientsInfo info : mTargets.values()) {
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 14cdb08..2b22f08 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -13,6 +13,16 @@
]
},
{
+ "file_patterns": [
+ "BinderDeathDispatcher\\.java"
+ ],
+ "name": "FrameworksCoreTests",
+ "options": [
+ { "include-filter": "com.android.internal.os.BinderDeathDispatcherTest" },
+ { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+ ]
+ },
+ {
"file_patterns": ["Battery[^/]*\\.java"],
"name": "FrameworksServicesTests",
"options": [
diff --git a/core/java/com/android/internal/util/StringPool.java b/core/java/com/android/internal/util/StringPool.java
new file mode 100644
index 0000000..c5180a3
--- /dev/null
+++ b/core/java/com/android/internal/util/StringPool.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+/**
+ * A pool of string instances. Unlike the {@link String#intern() VM's
+ * interned strings}, this pool provides no guarantee of reference equality.
+ * It is intended only to save allocations. This class is not thread safe.
+ *
+ * @hide
+ */
+public final class StringPool {
+
+ private final String[] mPool = new String[512];
+
+ /**
+ * Constructs string pool.
+ */
+ public StringPool() {
+ }
+
+ private static boolean contentEquals(String s, char[] chars, int start, int length) {
+ if (s.length() != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (chars[start + i] != s.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a string equal to {@code new String(array, start, length)}.
+ *
+ * @param array buffer containing string chars
+ * @param start offset in {@code array} where string starts
+ * @param length length of string
+ * @return string equal to {@code new String(array, start, length)}
+ */
+ public String get(char[] array, int start, int length) {
+ // Compute an arbitrary hash of the content
+ int hashCode = 0;
+ for (int i = start; i < start + length; i++) {
+ hashCode = (hashCode * 31) + array[i];
+ }
+
+ // Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
+ hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
+ hashCode ^= (hashCode >>> 7) ^ (hashCode >>> 4);
+ int index = hashCode & (mPool.length - 1);
+
+ String pooled = mPool[index];
+ if (pooled != null && contentEquals(pooled, array, start, length)) {
+ return pooled;
+ }
+
+ String result = new String(array, start, length);
+ mPool[index] = result;
+ return result;
+ }
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index de4cede..6755be1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -638,6 +638,7 @@
char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
+ char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];
char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeVdex[
sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
@@ -867,6 +868,9 @@
parseRuntimeOption("dalvik.vm.ps-resolved-classes-delay-ms", saveResolvedClassesDelayMsOptsBuf,
"-Xps-save-resolved-classes-delay-ms:");
+ parseRuntimeOption("dalvik.vm.ps-min-save-period-ms", profileMinSavePeriodOptsBuf,
+ "-Xps-min-save-period-ms:");
+
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
addOption("-XX:LowMemoryMode");
diff --git a/core/res/res/drawable/ic_accessibility_one_handed.xml b/core/res/res/drawable/ic_accessibility_one_handed.xml
new file mode 100644
index 0000000..0a469ba
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_one_handed.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+<background android:drawable="@color/accessibility_feature_background" />
+<foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_one_handed_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+</foreground>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_accessibility_one_handed_foreground.xml b/core/res/res/drawable/ic_accessibility_one_handed_foreground.xml
new file mode 100644
index 0000000..18e5618
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_one_handed_foreground.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path android:fillColor="@android:color/white"
+ android:pathData="M4.64169 3C3.88567 3 3.27271 3.61296 3.27271 4.36898V18.4011C3.27271 19.154 3.88875 19.7701 4.64169 19.7701H12.5339C12.5339 19.7701 12.5425 18.0588 11.2324 18.0588H6.01067C5.44597 18.0588 4.98393 17.5968 4.98393 17.0321V5.73797C4.98393 5.17326 5.44597 4.71123 6.01067 4.71123H15.9358C16.5005 4.71123 16.9625 5.17326 16.9625 5.73797V9.84492C16.9625 10.9651 16.4899 12.5952 15.5936 13.2674L9.77538 9.16043C8.58505 10.425 8.88177 11.705 10.1176 12.9251L13.1978 16.0053C13.1978 16.0053 13.3231 17.4572 13.5401 18.0588C14.2034 19.8984 16.2781 20.7968 16.2781 20.7968H19.231H19.2967C20.0835 20.7968 20.7273 20.153 20.7273 19.3662V12.2543L18.9441 4.06781C18.7662 3.23718 18.4068 3 17.8729 3H4.64169Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml
new file mode 100755
index 0000000..db438f2
--- /dev/null
+++ b/core/res/res/values-es-rMX/donottranslate-cldr.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="month_day_year">%-e %B %Y</string>
+ <string name="time_of_day">%H:%M:%S</string>
+ <string name="date_and_time">%-e %b %Y, %H:%M:%S</string>
+ <string name="date_time">%1$s, %2$s</string>
+</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6be6167..ade8516 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -936,7 +936,7 @@
<dimen name="system_app_widget_background_radius">16dp</dimen>
<!-- System-provided radius for inner views on app widgets. The resolved value of this resource may change at runtime. -->
<dimen name="system_app_widget_inner_radius">8dp</dimen>
- <!-- System-provided padding for inner views on app widgets. The resolved value of this resource may change at runtime. -->
- <dimen name="system_app_widget_internal_padding">16dp</dimen>
+ <!-- System-provided padding for inner views on app widgets. The resolved value of this resource may change at runtime. @removed -->
+ <dimen name="__removed_system_app_widget_internal_padding">16dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 03701c3..500a9da 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3184,7 +3184,7 @@
<!-- System-provided dimensions for app widgets. -->
<public name="system_app_widget_background_radius" />
<public name="system_app_widget_inner_radius" />
- <public name="system_app_widget_internal_padding" />
+ <public name="__removed_system_app_widget_internal_padding" />
</staging-public-group>
<staging-public-group type="bool" first-id="0x01110007">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a0c6f45..e86d2ce 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4636,6 +4636,9 @@
shown in the warning dialog about the accessibility shortcut. -->
<string name="color_correction_feature_name">Color Correction</string>
+ <!-- Title of One Handed Mode feature, shown in the system gestures of the accessibility shortcut. [CHAR LIMIT=none] -->
+ <string name="one_handed_mode_feature_name">One-Handed mode</string>
+
<!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
<string name="reduce_bright_colors_feature_name">Extra dim</string>
@@ -5007,10 +5010,10 @@
<string name="confirm_battery_saver">OK</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
- <string name="battery_saver_description_with_learn_more">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and certain features.\n\n<annotation id="url">Learn more</annotation></string>
+ <string name="battery_saver_description_with_learn_more">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features, and some network connections.\n\n<annotation id="url">Learn more</annotation></string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
- <string name="battery_saver_description">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, and certain features.</string>
+ <string name="battery_saver_description">Battery Saver turns on Dark theme and limits or turns off background activity, some visual effects, certain features, and some network connections.</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5019cca..3a22efc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3410,9 +3410,12 @@
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
+ <java-symbol type="drawable" name="ic_accessibility_one_handed" />
<java-symbol type="string" name="reduce_bright_colors_feature_name" />
+ <java-symbol type="string" name="one_handed_mode_feature_name" />
+
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
<java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 942045c..8310333 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -31,14 +32,14 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.FileDescriptor;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BinderDeathDispatcherTest {
@@ -120,7 +121,7 @@
public void die() {
isAlive = false;
if (mRecipient != null) {
- mRecipient.binderDied();
+ mRecipient.binderDied(this);
}
mRecipient = null;
}
@@ -227,33 +228,33 @@
// Kill the targets.
t1.die();
- verify(r1, times(1)).binderDied();
- verify(r2, times(1)).binderDied();
- verify(r3, times(1)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(0)).binderDied();
+ verify(r1, times(1)).binderDied(t1);
+ verify(r2, times(1)).binderDied(t1);
+ verify(r3, times(1)).binderDied(t1);
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(0)).binderDied(any());
assertThat(d.getTargetsForTest().size()).isEqualTo(2);
reset(r1, r2, r3, r4, r5);
t2.die();
- verify(r1, times(1)).binderDied();
- verify(r2, times(0)).binderDied();
- verify(r3, times(0)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(0)).binderDied();
+ verify(r1, times(1)).binderDied(t2);
+ verify(r2, times(0)).binderDied(any());
+ verify(r3, times(0)).binderDied(any());
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(0)).binderDied(any());
assertThat(d.getTargetsForTest().size()).isEqualTo(1);
reset(r1, r2, r3, r4, r5);
t3.die();
- verify(r1, times(0)).binderDied();
- verify(r2, times(0)).binderDied();
- verify(r3, times(1)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(1)).binderDied();
+ verify(r1, times(0)).binderDied(any());
+ verify(r2, times(0)).binderDied(any());
+ verify(r3, times(1)).binderDied(t3);
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(1)).binderDied(t3);
assertThat(d.getTargetsForTest().size()).isEqualTo(0);
@@ -262,4 +263,27 @@
assertThat(d.getTargetsForTest().size()).isEqualTo(0);
}
+
+ @Test
+ public void duplicateRegistrations() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+
+ for (int i = 0; i < 5; i++) {
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ }
+ assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
+
+ t1.die();
+ verify(r1, times(1)).binderDied(t1);
+ verify(r2, times(1)).binderDied(t1);
+
+ d.unlinkToDeath(t1, r1);
+ d.unlinkToDeath(t1, r2);
+ assertThat(d.getTargetsForTest()).isEmpty();
+ }
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java
new file mode 100644
index 0000000..f67fd51
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public final class StringPoolTest extends AndroidTestCase {
+
+ public void testStringPool() {
+ StringPool stringPool = new StringPool();
+ String bcd = stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
+ assertEquals("bcd", bcd);
+ assertSame(bcd, stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3));
+ }
+
+ public void testHashCollision() {
+ StringPool stringPool = new StringPool();
+ char[] a = { (char) 1, (char) 0 };
+ char[] b = { (char) 0, (char) 31 };
+ assertEquals(new String(a).hashCode(), new String(b).hashCode());
+
+ String aString = stringPool.get(a, 0, 2);
+ assertEquals(new String(a), aString);
+ String bString = stringPool.get(b, 0, 2);
+ assertEquals(new String(b), bString);
+ assertSame(bString, stringPool.get(b, 0, 2));
+ assertNotSame(aString, stringPool.get(a, 0, 2));
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index cff7dcc..a03931b 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -369,7 +369,9 @@
final PackageManager pm = context.getPackageManager();
try {
ApplicationInfo ai = pm.getApplicationInfo(
- resPackage, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ resPackage,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.GET_SHARED_LIBRARY_FILES);
if (ai != null) {
mObj1 = pm.getResourcesForApplication(ai);
} else {
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index e8757b5..edf000b 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -52,9 +52,6 @@
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
- <!-- Animation duration when exit starting window: icon going away -->
- <integer name="starting_window_icon_exit_anim_duration">166</integer>
-
<!-- Animation duration when exit starting window: reveal app -->
<integer name="starting_window_app_reveal_anim_duration">333</integer>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index ae7ab52..ce57638 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -135,6 +135,7 @@
}
};
+ private final ContentObserver mActivatedObserver;
private final ContentObserver mEnabledObserver;
private final ContentObserver mTimeoutObserver;
private final ContentObserver mTaskChangeExitObserver;
@@ -170,11 +171,13 @@
@Override
public void onStartFinished(Rect bounds) {
mState.setState(STATE_ACTIVE);
+ notifyShortcutState(STATE_ACTIVE);
}
@Override
public void onStopFinished(Rect bounds) {
mState.setState(STATE_NONE);
+ notifyShortcutState(STATE_NONE);
}
};
@@ -287,6 +290,7 @@
context.getContentResolver(), mUserId);
mTimeoutHandler = timeoutHandler;
+ mActivatedObserver = getObserver(this::onActivatedActionChanged);
mEnabledObserver = getObserver(this::onEnabledSettingChanged);
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
@@ -348,6 +352,12 @@
}
@VisibleForTesting
+ void notifyShortcutState(@OneHandedState.State int state) {
+ mOneHandedSettingsUtil.setOneHandedModeActivated(
+ mContext.getContentResolver(), state == STATE_ACTIVE ? 1 : 0, mUserId);
+ }
+
+ @VisibleForTesting
void startOneHanded() {
if (isLockedDisabled()) {
Slog.d(TAG, "Temporary lock disabled");
@@ -416,6 +426,9 @@
}
private void registerSettingObservers(int newUserId) {
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
+ mContext.getContentResolver(), mActivatedObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_ENABLED,
mContext.getContentResolver(), mEnabledObserver, newUserId);
mOneHandedSettingsUtil.registerSettingsKeyObserver(Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
@@ -466,6 +479,26 @@
}
@VisibleForTesting
+ void onActivatedActionChanged() {
+ if (mState.isTransitioning() || !isOneHandedEnabled()) {
+ return;
+ }
+
+ final boolean isActivated = mState.getState() == STATE_ACTIVE;
+ final boolean requestActivated = mOneHandedSettingsUtil.getOneHandedModeActivated(
+ mContext.getContentResolver(), mUserId);
+ // When gesture trigger action, we will update settings and introduce observer callback
+ // again, then the following logic will just ignore the second redundant callback.
+ if (isActivated ^ requestActivated) {
+ if (requestActivated) {
+ startOneHanded();
+ } else {
+ stopOneHanded();
+ }
+ }
+ }
+
+ @VisibleForTesting
void onEnabledSettingChanged() {
final boolean enabled = mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver(), mUserId);
@@ -544,6 +577,11 @@
return mLockedDisabled;
}
+ @VisibleForTesting
+ boolean isOneHandedEnabled() {
+ return mIsOneHandedEnabled;
+ }
+
private void updateOneHandedEnabled() {
if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
mMainExecutor.execute(() -> stopOneHanded());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 2ab51f3..bb68224 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -131,8 +131,28 @@
* Returns whether swipe bottom to notification gesture enabled or not.
*/
public boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver, int userId) {
- return Settings.Secure.getInt(resolver,
- Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0 /* Default OFF */) == 1;
+ return Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1;
+ }
+
+ /**
+ * Sets one handed activated or not to notify state for shortcut
+ *
+ * @return activated or not
+ */
+ public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) {
+ return Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED, 0, userId) == 1;
+ }
+
+ /**
+ * Sets one handed activated or not to notify state for shortcut
+ *
+ * @return activated or not
+ */
+ public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) {
+ return Settings.Secure.putIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId);
}
void dump(PrintWriter pw, String prefix, ContentResolver resolver,
@@ -145,6 +165,8 @@
pw.println(getSettingsOneHandedModeTimeout(resolver, userId));
pw.print(innerPrefix + "tapsAppToExit=");
pw.println(getSettingsTapsAppToExit(resolver, userId));
+ pw.print(innerPrefix + "shortcutActivated=");
+ pw.println(getOneHandedModeActivated(resolver, userId));
}
public OneHandedSettingsUtil() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 5bc2afd..3fe57c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.startingsurface;
+import static android.view.Choreographer.CALLBACK_COMMIT;
import static android.view.View.GONE;
import android.animation.Animator;
@@ -29,13 +30,12 @@
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.Slog;
+import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.Transformation;
@@ -53,51 +53,36 @@
private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
private static final String TAG = StartingSurfaceDrawer.TAG;
- private static final Interpolator ICON_EXIT_INTERPOLATOR = new PathInterpolator(1f, 0f, 1f, 1f);
private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
- private static final int EXTRA_REVEAL_DELAY = 133;
private final Matrix mTmpTransform = new Matrix();
- private final float[] mTmpFloat9 = new float[9];
- private SurfaceControl mFirstWindowSurface;
+ private final SurfaceControl mFirstWindowSurface;
private final Rect mFirstWindowFrame = new Rect();
private final SplashScreenView mSplashScreenView;
private final int mMainWindowShiftLength;
- private final int mIconShiftLength;
private final int mAppDuration;
- private final int mIconDuration;
private final TransactionPool mTransactionPool;
private ValueAnimator mMainAnimator;
- private Animation mShiftUpAnimation;
- private AnimationSet mIconAnimationSet;
+ private ShiftUpAnimation mShiftUpAnimation;
private Runnable mFinishCallback;
SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame,
- int appDuration, int iconDuration, int mainWindowShiftLength, int iconShiftLength,
- TransactionPool pool, Runnable handleFinish) {
+ int appDuration, int mainWindowShiftLength, TransactionPool pool,
+ Runnable handleFinish) {
mSplashScreenView = view;
mFirstWindowSurface = leash;
if (frame != null) {
mFirstWindowFrame.set(frame);
}
mAppDuration = appDuration;
- mIconDuration = iconDuration;
mMainWindowShiftLength = mainWindowShiftLength;
- mIconShiftLength = iconShiftLength;
mFinishCallback = handleFinish;
mTransactionPool = pool;
}
- void prepareAnimations() {
- prepareRevealAnimation();
- prepareShiftAnimation();
- }
-
void startAnimations() {
- if (mIconAnimationSet != null) {
- mIconAnimationSet.start();
- }
+ prepareRevealAnimation();
if (mMainAnimator != null) {
mMainAnimator.start();
}
@@ -114,8 +99,7 @@
mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR);
mMainAnimator.addListener(this);
- final int startDelay = mIconDuration + EXTRA_REVEAL_DELAY;
- final float transparentRatio = 0.95f;
+ final float transparentRatio = 0.8f;
final int globalHeight = mSplashScreenView.getHeight();
final int verticalCircleCenter = 0;
final int finalVerticalLength = globalHeight - verticalCircleCenter;
@@ -130,9 +114,8 @@
final float[] stops = {0f, transparentRatio, 1f};
radialVanishAnimation.setRadialPaintParam(colors, stops);
radialVanishAnimation.setReady();
- mMainAnimator.setStartDelay(startDelay);
- if (mFirstWindowSurface != null) {
+ if (mFirstWindowSurface != null && mFirstWindowSurface.isValid()) {
// shift up main window
View occludeHoleView = new View(mSplashScreenView.getContext());
if (DEBUG_EXIT_ANIMATION_BLEND) {
@@ -144,59 +127,16 @@
WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
mSplashScreenView.addView(occludeHoleView, params);
- mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength);
+ mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView);
mShiftUpAnimation.setDuration(mAppDuration);
mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR);
- mShiftUpAnimation.setStartOffset(startDelay);
occludeHoleView.setAnimation(mShiftUpAnimation);
}
}
- // shift down icon and branding view
- private void prepareShiftAnimation() {
- final View iconView = mSplashScreenView.getIconView();
- if (iconView == null) {
- return;
- }
- if (mIconShiftLength > 0) {
- mIconAnimationSet = new AnimationSet(true /* shareInterpolator */);
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "first exit animation, shift length: " + mIconShiftLength);
- }
- mIconAnimationSet.addAnimation(new TranslateYAnimation(0, mIconShiftLength));
- mIconAnimationSet.addAnimation(new AlphaAnimation(1, 0));
- mIconAnimationSet.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
-
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- if (DEBUG_EXIT_ANIMATION) {
- Slog.v(TAG, "first exit animation finished");
- }
- iconView.post(() -> iconView.setVisibility(GONE));
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- // ignore
- }
- });
- mIconAnimationSet.setDuration(mIconDuration);
- mIconAnimationSet.setInterpolator(ICON_EXIT_INTERPOLATOR);
- iconView.setAnimation(mIconAnimationSet);
- final View brandingView = mSplashScreenView.getBrandingView();
- if (brandingView != null) {
- brandingView.setAnimation(mIconAnimationSet);
- }
- }
- }
-
private static class RadialVanishAnimation extends View {
- private SplashScreenView mView;
+ private final SplashScreenView mView;
private int mInitRadius;
private int mFinishRadius;
private boolean mReady;
@@ -217,7 +157,7 @@
mVanishMatrix.setScale(scale, scale);
mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
- mView.postInvalidate();
+ postInvalidate();
});
mView.addView(this);
}
@@ -262,28 +202,57 @@
}
private final class ShiftUpAnimation extends TranslateYAnimation {
- ShiftUpAnimation(float fromYDelta, float toYDelta) {
+ final SyncRtSurfaceTransactionApplier mApplier;
+ ShiftUpAnimation(float fromYDelta, float toYDelta, View targetView) {
super(fromYDelta, toYDelta);
+ mApplier = new SyncRtSurfaceTransactionApplier(targetView);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
- if (mFirstWindowSurface == null) {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
return;
}
mTmpTransform.set(t.getMatrix());
+
+ // set the vsyncId to ensure the transaction doesn't get applied too early.
final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
mTmpTransform.postTranslate(mFirstWindowFrame.left,
mFirstWindowFrame.top + mMainWindowShiftLength);
- tx.setMatrix(mFirstWindowSurface, mTmpTransform, mTmpFloat9);
- // TODO set the vsyncId to ensure the transaction doesn't get applied too early.
- // Additionally, do you want to have this synchronized with your view animations?
- // If so, you'll need to use SyncRtSurfaceTransactionApplier
- tx.apply();
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withMatrix(mTmpTransform)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+
mTransactionPool.release(tx);
}
+
+ void finish() {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+ return;
+ }
+ final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withWindowCrop(null)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+ mTransactionPool.release(tx);
+
+ Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
+ mFirstWindowSurface::release, null);
+ }
}
private void reset() {
@@ -297,12 +266,8 @@
mFinishCallback = null;
}
});
- if (mFirstWindowSurface != null) {
- final SurfaceControl.Transaction tx = mTransactionPool.acquire();
- tx.setWindowCrop(mFirstWindowSurface, null);
- tx.apply();
- mFirstWindowSurface.release();
- mFirstWindowSurface = null;
+ if (mShiftUpAnimation != null) {
+ mShiftUpAnimation.finish();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 7a33f50..e656f43 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -75,20 +75,15 @@
private int mBrandingImageWidth;
private int mBrandingImageHeight;
private final int mAppRevealDuration;
- private final int mIconExitDuration;
private int mMainWindowShiftLength;
- private int mIconNormalExitDistance;
- private int mIconEarlyExitDistance;
private final TransactionPool mTransactionPool;
private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
private final Handler mSplashscreenWorkerHandler;
- SplashscreenContentDrawer(Context context, int iconExitAnimDuration, int appRevealAnimDuration,
- TransactionPool pool) {
+ SplashscreenContentDrawer(Context context, int appRevealAnimDuration, TransactionPool pool) {
mContext = context;
mIconProvider = new IconProvider(context);
mAppRevealDuration = appRevealAnimDuration;
- mIconExitDuration = iconExitAnimDuration;
mTransactionPool = pool;
// Initialize Splashscreen worker thread
@@ -139,10 +134,6 @@
com.android.wm.shell.R.dimen.starting_surface_brand_image_height);
mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
- mIconNormalExitDistance = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.starting_surface_normal_exit_icon_distance);
- mIconEarlyExitDistance = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.starting_surface_early_exit_icon_distance);
}
private int getSystemBGColor() {
@@ -245,7 +236,7 @@
private SplashScreenView mCachedResult;
private int mThemeColor;
private Drawable mFinalIconDrawable;
- private float mScale = 1f;
+ private int mFinalIconSize = mIconSize;
StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) {
mThemeColor = background;
@@ -282,48 +273,46 @@
Drawable iconDrawable;
final int animationDuration;
- final int iconSize;
if (mEmptyView) {
// empty splash screen case
animationDuration = 0;
- iconSize = 0;
+ mFinalIconSize = 0;
} else if (mTmpAttrs.mReplaceIcon != null) {
// replaced icon, don't process
iconDrawable = mTmpAttrs.mReplaceIcon;
animationDuration = mTmpAttrs.mAnimationDuration;
- createIconDrawable(iconDrawable, mIconSize);
- iconSize = mIconSize;
+ createIconDrawable(iconDrawable);
} else {
- final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
- iconDrawable = mIconProvider.getIcon(mActivityInfo);
+ final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
+ final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
+ final int scaledIconDpi = (int) (0.5f + iconScale * densityDpi);
+ iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
if (iconDrawable == null) {
iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
}
- if (!processAdaptiveIcon(iconDrawable, iconScale)) {
+ if (!processAdaptiveIcon(iconDrawable)) {
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
// TODO process legacy icon(bitmap)
- final Drawable tempIcon = loadIconByDensity(iconDrawable, iconScale);
- createIconDrawable(tempIcon, mIconSize);
+ createIconDrawable(iconDrawable);
}
animationDuration = 0;
- iconSize = (int) (0.5 + mIconSize * mScale);
}
- mCachedResult = fillViewWithIcon(iconSize, mFinalIconDrawable, animationDuration);
+ mCachedResult = fillViewWithIcon(mFinalIconSize, mFinalIconDrawable, animationDuration);
mBuildComplete = true;
return mCachedResult;
}
- private void createIconDrawable(Drawable iconDrawable, int iconSize) {
+ private void createIconDrawable(Drawable iconDrawable) {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
mTmpAttrs.mIconBgColor != Color.TRANSPARENT
? mTmpAttrs.mIconBgColor : mThemeColor,
- iconDrawable, iconSize, mSplashscreenWorkerHandler);
+ iconDrawable, mFinalIconSize, mSplashscreenWorkerHandler);
}
- private boolean processAdaptiveIcon(Drawable iconDrawable, float iconScale) {
+ private boolean processAdaptiveIcon(Drawable iconDrawable) {
if (!(iconDrawable instanceof AdaptiveIconDrawable)) {
return false;
}
@@ -334,7 +323,7 @@
final DrawableColorTester backIconTester =
new DrawableColorTester(adaptiveIconDrawable.getBackground());
- Drawable iconForeground = adaptiveIconDrawable.getForeground();
+ final Drawable iconForeground = adaptiveIconDrawable.getForeground();
final DrawableColorTester foreIconTester =
new DrawableColorTester(iconForeground, true /* filterTransparent */);
@@ -373,35 +362,20 @@
final float noBgScale =
foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD
? NO_BACKGROUND_SCALE : 1f;
- final Drawable tempIcon = loadIconByDensity(iconDrawable, iconScale * noBgScale);
- if (tempIcon instanceof AdaptiveIconDrawable) {
- mScale = noBgScale;
- iconForeground = ((AdaptiveIconDrawable) tempIcon).getForeground();
- }
// Using AdaptiveIconDrawable here can help keep the shape consistent with the
// current settings.
- final int iconSize = (int) (0.5f + mIconSize * mScale);
- createIconDrawable(iconForeground, iconSize);
+ mFinalIconSize = (int) (0.5f + mIconSize * noBgScale);
+ createIconDrawable(iconForeground);
} else {
if (DEBUG) {
Slog.d(TAG, "makeSplashScreenContentView: draw whole icon");
}
- final Drawable tempIcon = loadIconByDensity(iconDrawable, iconScale);
- createIconDrawable(tempIcon, mIconSize);
+ createIconDrawable(iconDrawable);
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return true;
}
- private Drawable loadIconByDensity(Drawable baseDrawable, float scale) {
- if (scale == 1 && baseDrawable != null) {
- return baseDrawable;
- }
- final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
- final int updateDpi = (int) (0.5f + scale * densityDpi);
- return mIconProvider.getIcon(mActivityInfo, updateDpi);
- }
-
private SplashScreenView fillViewWithIcon(int iconSize, Drawable iconDrawable,
int animationDuration) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon");
@@ -423,6 +397,7 @@
}
if (mEmptyView) {
splashScreenView.setNotCopyable();
+ splashScreenView.setRevealAnimationSupported(false);
}
splashScreenView.makeSystemUIColorsTransparent();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -695,12 +670,10 @@
* Create and play the default exit animation for splash screen view.
*/
void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
- Rect frame, boolean isEarlyExit, Runnable finishCallback) {
+ Rect frame, Runnable finishCallback) {
final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash,
- frame, mAppRevealDuration, mIconExitDuration, mMainWindowShiftLength,
- isEarlyExit ? mIconEarlyExitDistance : mIconNormalExitDistance, mTransactionPool,
+ frame, mAppRevealDuration, mMainWindowShiftLength, mTransactionPool,
finishCallback);
- animation.prepareAnimations();
animation.startAnimations();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 4273f89..b7a0339 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -35,7 +35,7 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
-import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
@@ -106,6 +106,8 @@
private final SplashscreenContentDrawer mSplashscreenContentDrawer;
private Choreographer mChoreographer;
+ private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION =
+ SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false);
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
*/
@@ -114,12 +116,10 @@
mContext = context;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
mSplashScreenExecutor = splashScreenExecutor;
- final int iconExitAnimDuration = context.getResources().getInteger(
- com.android.wm.shell.R.integer.starting_window_icon_exit_anim_duration);
final int appRevealAnimDuration = context.getResources().getInteger(
com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
- mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconExitAnimDuration,
- appRevealAnimDuration, pool);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, appRevealAnimDuration,
+ pool);
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
}
@@ -467,20 +467,24 @@
if (DEBUG_SPLASH_SCREEN) {
Slog.v(TAG, "Removing splash screen window for task: " + taskId);
}
- if (record.mContentView != null) {
- if (leash != null || playRevealAnimation) {
- mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
- leash, frame, record.isEarlyExit(),
- () -> removeWindowInner(record.mDecorView, true));
+ if (record.mContentView != null
+ && record.mContentView.isRevealAnimationSupported()) {
+ if (playRevealAnimation) {
+ if (DEBUG_ENABLE_REVEAL_ANIMATION) {
+ mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
+ leash, frame,
+ () -> removeWindowInner(record.mDecorView, true));
+ } else {
+ // using the default exit animation from framework
+ removeWindowInner(record.mDecorView, false);
+ }
} else {
- // TODO(183004107) Always hide decorView when playRevealAnimation is enabled
- // from TaskOrganizerController#removeStartingWindow
- // the SplashScreenView has been copied to client, skip default exit
- // animation
- removeWindowInner(record.mDecorView, false);
+ // the SplashScreenView has been copied to client, hide the view to skip
+ // default exit animation
+ removeWindowInner(record.mDecorView, true);
}
} else {
- // no animation will be applied
+ // this is a blank splash screen, don't apply reveal animation
removeWindowInner(record.mDecorView, false);
}
}
@@ -514,12 +518,10 @@
* Record the view or surface for a starting window.
*/
private static class StartingWindowRecord {
- private static final long EARLY_START_MINIMUM_TIME_MS = 250;
private final View mDecorView;
private final TaskSnapshotWindow mTaskSnapshotWindow;
private SplashScreenView mContentView;
private boolean mSetSplashScreen;
- private long mContentCreateTime;
StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
mDecorView = decorView;
@@ -531,12 +533,7 @@
return;
}
mContentView = splashScreenView;
- mContentCreateTime = SystemClock.uptimeMillis();
mSetSplashScreen = true;
}
-
- boolean isEarlyExit() {
- return SystemClock.uptimeMillis() - mContentCreateTime < EARLY_START_MINIMUM_TIME_MS;
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index c1282c9..99342f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -16,7 +16,9 @@
package com.android.wm.shell.onehanded;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import static com.google.common.truth.Truth.assertThat;
@@ -360,4 +362,71 @@
verify(mMockGestureHandler).onGestureEnabled(isOneHandedEnabled);
}
+
+ @Test
+ public void testStateActive_shortcutRequestActivate_skipActions() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testStateNotActive_shortcutRequestInActivate_skipAction() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testStateNotActive_shortcutRequestActivate_doAction() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testEnteringTransition_shortcutRequestActivate_skipActions() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_ENTERING);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testExitingTransition_shortcutRequestActivate_skipActions() {
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_EXITING);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
+
+ @Test
+ public void testOneHandedDisabled_shortcutEnabled_skipActions() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController, never()).startOneHanded();
+ verify(mSpiedOneHandedController, never()).stopOneHanded();
+ }
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 32413dc..6403aab 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -36,13 +36,20 @@
/**
* The SoundPool class manages and plays audio resources for applications.
*
- * <p>A SoundPool is a collection of samples that can be loaded into memory
+ * <p>A SoundPool is a collection of sound samples that can be loaded into memory
* from a resource inside the APK or from a file in the file system. The
- * SoundPool library uses the MediaPlayer service to decode the audio
- * into a raw 16-bit PCM mono or stereo stream. This allows applications
+ * SoundPool library uses the MediaCodec service to decode the audio
+ * into raw 16-bit PCM. This allows applications
* to ship with compressed streams without having to suffer the CPU load
* and latency of decompressing during playback.</p>
*
+ * <p>Soundpool sounds are expected to be short as they are
+ * predecoded into memory. Each decoded sound is internally limited to one
+ * megabyte storage, which represents approximately 5.6 seconds at 44.1kHz stereo
+ * (the duration is proportionally longer at lower sample rates or
+ * a channel mask of mono). A decoded audio sound will be truncated if it would
+ * exceed the per-sound one megabyte storage space.</p>
+ *
* <p>In addition to low-latency playback, SoundPool can also manage the number
* of audio streams being rendered at once. When the SoundPool object is
* constructed, the maxStreams parameter sets the maximum number of streams
diff --git a/native/android/native_window_jni.cpp b/native/android/native_window_jni.cpp
index 859c550..901b4de 100644
--- a/native/android/native_window_jni.cpp
+++ b/native/android/native_window_jni.cpp
@@ -30,7 +30,7 @@
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) {
sp<ANativeWindow> win = android_view_Surface_getNativeWindow(env, surface);
if (win != NULL) {
- win->incStrong((void*)ANativeWindow_fromSurface);
+ ANativeWindow_acquire(win.get());
}
return win.get();
}
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index b0c8c61..a0f3098 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -506,7 +506,7 @@
}
int64_t AImageDecoderFrameInfo_getDuration(const AImageDecoderFrameInfo* info) {
- if (!info) return 0;
+ if (!info) return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
return toFrameInfo(info)->fDuration * 1'000'000;
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 95f180a..0210079 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -23,6 +23,7 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.StringRes;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -632,6 +633,12 @@
.setPositiveButton(R.string.ok, (dialog, which) -> getActivity().finish())
.create();
}
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().setResult(Activity.RESULT_CANCELED);
+ getActivity().finish();
+ }
}
/**
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
index d8b49ab..7d9b4d7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -43,7 +43,7 @@
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:scrimAnimationDuration="50"
app:scrimVisibleHeightTrigger="@dimen/scrim_visible_height_trigger"
- app:statusBarScrim="@empty"
+ app:statusBarScrim="@null"
app:titleCollapseMode="fade"
app:collapsedTitleTextAppearance="@style/CollapsingToolbarTitle.Collapsed"
app:expandedTitleTextAppearance="@style/CollapsingToolbarTitle.Expanded"
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 49152e4..11f7db7 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -451,10 +451,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"La tablette va bientôt s\'éteindre (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"L\'appareil va bientôt s\'éteindre (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_charging_duration_only (8085099012811384899) -->
- <skip />
- <!-- no translation found for power_charging_duration (6127154952524919719) -->
- <skip />
+ <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Chargée à 100 %% dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Optimisation pour préserver batterie"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 24b2cb8..edf653f 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -576,8 +576,8 @@
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necesario reiniciar o teu dispositivo para aplicar este cambio. Reiníciao agora ou cancela o cambio."</string>
<string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Auriculares con cable"</string>
- <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activado"</string>
- <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivado"</string>
+ <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activada"</string>
+ <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivada"</string>
<string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de rede do operador"</string>
<string name="data_connection_3g" msgid="931852552688157407">"3G"</string>
<string name="data_connection_edge" msgid="4625509456544797637">"EDGE"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index c07af12..c312456 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -548,7 +548,7 @@
<string name="user_add_profile_item_title" msgid="3111051717414643029">"प्रतिबंधित प्रोफ़ाइल"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"नया उपयोगकर्ता जोड़ें?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"आप और ज़्यादा उपयोगकर्ता बनाकर इस डिवाइस को दूसरे लोगों के साथ शेयर कर सकते हैं. हर उपयोगकर्ता के पास अपनी जगह होती है, जिसमें वह मनपसंद तरीके से ऐप्लिकेशन, वॉलपेपर और दूसरी चीज़ों में बदलाव कर सकते हैं. उपयोगकर्ता वाई-फ़ाई जैसी डिवाइस सेटिंग में भी बदलाव कर सकते हैं, जिसका असर हर किसी पर पड़ेगा.\n\nजब आप कोई नया उपयोगकर्ता जोड़ते हैं तो उन्हें अपनी जगह सेट करनी होगी.\n\nकोई भी उपयोगकर्ता दूसरे सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है. ऐसा भी हो सकता है कि सुलभता सेटिंग और सेवाएं नए उपयोगकर्ता को ट्रांसफ़र न हो पाएं."</string>
- <string name="user_add_user_message_short" msgid="3295959985795716166">"जब आप कोई नया उपयोगकर्ता जोड़ते हैं तो उसे अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता बाकी सभी उपयोगकर्ताओं के लिए ऐप अपडेट कर सकता है."</string>
+ <string name="user_add_user_message_short" msgid="3295959985795716166">"जब आप कोई नया उपयोगकर्ता जोड़ते हैं, तो उसे अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"उपयोगकर्ता को अभी सेट करें?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"पक्का करें कि व्यक्ति डिवाइस का इस्तेमाल करने और अपनी जगह सेट करने के लिए मौजूद है"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"प्रोफ़ाइल अभी सेट करें?"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a29462a..798531a 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -565,8 +565,8 @@
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string>
<string name="add_user_failed" msgid="4809887794313944872">"สร้างผู้ใช้ใหม่ไม่ได้"</string>
<string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
- <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
+ <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
diff --git a/packages/SystemUI/docs/QS-QQS.png b/packages/SystemUI/docs/QS-QQS.png
index 02de479..3ed8e24 100644
--- a/packages/SystemUI/docs/QS-QQS.png
+++ b/packages/SystemUI/docs/QS-QQS.png
Binary files differ
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index b48ba67..5216209 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -8,7 +8,7 @@
## What are Quick Settings Tiles?
-Quick Settings (from now on, QS) is the expanded panel that contains shortcuts for the user to toggle many settings. This is opened by expanding the notification drawer twice (or once when phone is locked). Quick Quick Settings (QQS) is the smaller panel that appears on top of the notifications before expanding twice and contains some of the toggles with no text.
+Quick Settings (from now on, QS) is the expanded panel that contains shortcuts for the user to toggle many settings. This is opened by expanding the notification drawer twice (or once when phone is locked). Quick Quick Settings (QQS) is the smaller panel that appears on top of the notifications before expanding twice and contains some of the toggles with no secondary line.
Each of these toggles that appear either in QS or QQS are called Quick Settings Tiles (or tiles for short). They allow the user to enable or disable settings quickly and sometimes provides access to more comprehensive settings pages.
@@ -69,13 +69,13 @@
Each Tile has a couple of associated views for displaying it in QS and QQS. These views are updated after the backend updates the `State` using `QSTileImpl#handleUpdateState`.
-* **[`com.android.systemui.plugins.qs.QSTileView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java)**: Abstract class that provides basic Tile functionality. These allows external [Factories](#qsfactory) to create Tiles.
-* **[`QSTileBaseView`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java)**: Implementation of `QSTileView` used in QQS that takes care of most of the features of the view:
+* **[`QSTileView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java)**: Abstract class that provides basic Tile functionality. These allows external [Factories](#qsfactory) to create Tiles.
+* **[`QSTileViewImpl`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.java)**: Implementation of `QSTileView`. It takes care of the following:
* Holding the icon
* Background color and shape
* Ripple
* Click listening
-* **[`QSTileView`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java)**: Extends `QSTileBaseView`to add label support. Used in QS.
+ * Labels
* **[`QSIconView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java)**
* **[`QSIconViewImpl`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java)**
@@ -103,7 +103,7 @@
This is a brief run-down of what happens when a user clicks on a tile. Internal changes on the device (for example, changes from Settings) will trigger this process starting in step 3. Throughout this section, we assume that we are dealing with a `QSTileImpl`.
1. User clicks on tile. The following calls happen in sequence:
- 1. `QSTileBaseView#onClickListener`.
+ 1. `QSTileViewImpl#onClickListener`.
2. `QSTile#click`.
3. `QSTileImpl#handleClick`. This last call sets the new state for the device by using the associated controller.
2. State in the device changes. This is normally outside of SystemUI's control.
@@ -113,9 +113,9 @@
4. `QSTileImpl#handleUpdateState` is called to update the state with the new information. This information can be obtained both from the `Object` passed to `refreshState` as well as from the controller.
5. If the state has changed (in at least one element), `QSTileImpl#handleStateChanged` is called. This will trigger a call to all the associated `QSTile.Callback#onStateChanged`, passing the new `State`.
6. `QSTileView#onStateChanged` is called and this calls `QSTileView#handleStateChanged`. This method maps the state into the view:
- * The tile is rippled and the color changes to match the new state.
+ * The tile colors change to match the new state.
* `QSIconView.setIcon` is called to apply the correct state to the icon and the correct icon to the view.
- * If the tile is a `QSTileView` (in expanded QS), the labels are changed.
+ * The tile labels change to match the new state.
## Third party tiles (TileService)
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 9bb2dde..69ce275 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,10 +20,13 @@
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.os.Parcelable;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.systemui.plugins.annotations.ProvidesInterface;
import java.util.List;
@@ -50,6 +53,11 @@
return null;
}
+ /**
+ * As the smartspace view becomes available, allow listeners to receive an event.
+ */
+ default void addOnAttachStateChangeListener(View.OnAttachStateChangeListener listener) { }
+
/** Updates Smartspace data and propagates it to any listeners. */
void onTargetsAvailable(List<SmartspaceTarget> targets);
@@ -83,6 +91,11 @@
* When on the lockscreen, use the FalsingManager to help detect errant touches
*/
void setFalsingManager(com.android.systemui.plugins.FalsingManager falsingManager);
+
+ /**
+ * Set or clear any Do Not Disturb information.
+ */
+ void setDnd(@Nullable Icon dndIcon, @Nullable String description);
}
/** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index d1383eb..55dea3d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -163,7 +163,7 @@
public SlashState slash;
public boolean handlesLongClick = true;
public boolean showRippleEffect = true;
- public Drawable sideViewDrawable;
+ public Drawable sideViewCustomDrawable;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -185,7 +185,7 @@
|| !Objects.equals(other.slash, slash)
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
|| !Objects.equals(other.showRippleEffect, showRippleEffect)
- || !Objects.equals(other.sideViewDrawable, sideViewDrawable);
+ || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
other.icon = icon;
other.iconSupplier = iconSupplier;
other.label = label;
@@ -201,7 +201,7 @@
other.slash = slash != null ? slash.copy() : null;
other.handlesLongClick = handlesLongClick;
other.showRippleEffect = showRippleEffect;
- other.sideViewDrawable = sideViewDrawable;
+ other.sideViewCustomDrawable = sideViewCustomDrawable;
return changed;
}
@@ -227,7 +227,7 @@
sb.append(",isTransient=").append(isTransient);
sb.append(",state=").append(state);
sb.append(",slash=\"").append(slash).append("\"");
- sb.append(",sideViewDrawable").append(sideViewDrawable);
+ sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
return sb.append(']');
}
@@ -242,12 +242,16 @@
public static class BooleanState extends State {
public static final int VERSION = 1;
public boolean value;
+ public boolean forceExpandIcon;
@Override
public boolean copyTo(State other) {
final BooleanState o = (BooleanState) other;
- final boolean changed = super.copyTo(other) || o.value != value;
+ final boolean changed = super.copyTo(other)
+ || o.value != value
+ || o.forceExpandIcon != forceExpandIcon;
o.value = value;
+ o.forceExpandIcon = forceExpandIcon;
return changed;
}
@@ -255,6 +259,7 @@
protected StringBuilder toStringBuilder() {
final StringBuilder rt = super.toStringBuilder();
rt.insert(rt.length() - 1, ",value=" + value);
+ rt.insert(rt.length() - 1, ",forceExpandIcon=" + forceExpandIcon);
return rt;
}
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 1cef44b..7c5459c1 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -80,6 +80,6 @@
android:layout_width="match_parent"
android:layout_height="@dimen/notification_shelf_height"
android:layout_below="@id/keyguard_status_area"
- android:paddingStart="@dimen/below_clock_padding_start"
+ android:paddingStart="@dimen/below_clock_padding_start_extra"
/>
</com.android.keyguard.KeyguardClockSwitch>
diff --git a/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml b/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml
deleted file mode 100644
index 1be489c..0000000
--- a/packages/SystemUI/res-keyguard/layout/qs_media_divider.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_marginBottom="16dp"
- android:background="@color/media_divider">
-</View>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 9b8035d..a166b09 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -94,4 +94,5 @@
<!-- additional offset for clock switch area items -->
<dimen name="clock_padding_start">28dp</dimen>
<dimen name="below_clock_padding_start">32dp</dimen>
+ <dimen name="below_clock_padding_start_extra">36dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_blank.xml b/packages/SystemUI/res/drawable/ic_blank.xml
new file mode 100644
index 0000000..b94088f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_blank.xml
@@ -0,0 +1,22 @@
+<!--
+Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="16dp"
+ android:height="16dp"
+ android:viewportWidth="16.0"
+ android:viewportHeight="16.0">
+ <path/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_sim_card.xml b/packages/SystemUI/res/drawable/ic_qs_sim_card.xml
new file mode 100644
index 0000000..6eda929
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_sim_card.xml
@@ -0,0 +1,47 @@
+<!--
+Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal"
+ >
+
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,2h-8L4,8v12c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V4C20,2.9,19.1,2,18,2z M18,4v16H6V8.83L10.83,4L18,4L18,4z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M 7 17 H 9 V 19 H 7 V 17 Z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M 15 17 H 17 V 19 H 15 V 17 Z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M 7 11 H 9 V 15 H 7 V 11 Z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M 11 15 H 13 V 19 H 11 V 15 Z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M 11 11 H 13 V 13 H 11 V 11 Z" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M 15 11 H 17 V 15 H 15 V 11 Z" />
+ <path
+ android:pathData="M0,0h24v24H0V0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index 9f41dbe..99c0ab4b 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,6 +16,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@color/privacy_circle" />
+ <solid android:color="@color/privacy_chip_background" />
<corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml b/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
index 54a66e2..f4ff549 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_camera.xml
@@ -24,7 +24,7 @@
android:height="@dimen/ongoing_appops_dialog_circle_size"
android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
- <solid android:color="@color/privacy_circle" />
+ <solid android:color="@color/privacy_chip_background" />
</shape>
</item>
<item android:id="@id/icon"
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_location.xml b/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
index 65f4396..9a201886 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_location.xml
@@ -24,7 +24,7 @@
android:height="@dimen/ongoing_appops_dialog_circle_size"
android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
- <solid android:color="@color/privacy_circle" />
+ <solid android:color="@color/privacy_chip_background" />
</shape>
</item>
<item android:id="@id/icon"
diff --git a/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml b/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
index 1565d2d..a5f2d32 100644
--- a/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
+++ b/packages/SystemUI/res/drawable/privacy_item_circle_microphone.xml
@@ -24,7 +24,7 @@
android:height="@dimen/ongoing_appops_dialog_circle_size"
android:width="@dimen/ongoing_appops_dialog_circle_size"
/>
- <solid android:color="@color/privacy_circle" />
+ <solid android:color="@color/privacy_chip_background" />
</shape>
</item>
<item android:id="@id/icon"
diff --git a/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
index 4e9d380..9f81b0d 100644
--- a/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
+++ b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
@@ -18,7 +18,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
- android:color="@color/privacy_circle"/>
+ android:color="@color/privacy_chip_background"/>
<size
android:width="6dp"
android:height="6dp"
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
index a5e7f5d..a146547 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -29,18 +29,17 @@
android:src="@*android:drawable/ic_phone"
android:layout_width="@dimen/ongoing_call_chip_icon_size"
android:layout_height="@dimen/ongoing_call_chip_icon_size"
- android:paddingEnd="@dimen/ongoing_call_chip_icon_text_padding"
android:tint="?android:attr/colorPrimary"
/>
<!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. -->
- <!-- TODO(b/183229367): This text view's width shouldn't change as the time increases. -->
- <Chronometer
+ <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer
android:id="@+id/ongoing_call_chip_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
- android:gravity="center"
+ android:gravity="center|start"
+ android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textColor="?android:attr/colorPrimary"
diff --git a/packages/SystemUI/res/layout/qs_carrier_group.xml b/packages/SystemUI/res/layout/qs_carrier_group.xml
index fd53a8b..810c959 100644
--- a/packages/SystemUI/res/layout/qs_carrier_group.xml
+++ b/packages/SystemUI/res/layout/qs_carrier_group.xml
@@ -32,11 +32,10 @@
android:minWidth="48dp"
android:minHeight="48dp"
android:gravity="center_vertical"
- android:textAppearance="@style/TextAppearance.QS.Status"
+ android:textAppearance="@style/TextAppearance.QS.Status.NoCarrierText"
android:textDirection="locale"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
- android:maxEms="7"
android:visibility="gone"/>
<include
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 93a4715..266ecef 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -22,6 +22,7 @@
android:layout_height="@dimen/qs_footer_height"
android:layout_marginStart="@dimen/qs_footer_margin"
android:layout_marginEnd="@dimen/qs_footer_margin"
+ android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
android:background="@android:color/transparent"
android:baselineAligned="false"
android:clickable="false"
@@ -50,7 +51,6 @@
android:focusable="true"
android:gravity="center_vertical"
android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
android:textAppearance="@style/TextAppearance.QS.Build"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 7cf3d01..4607e5f 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -42,8 +42,6 @@
android:clipToPadding="false"
android:clipChildren="false">
<include layout="@layout/qs_footer_impl" />
- <include layout="@layout/qs_media_divider"
- android:id="@+id/divider"/>
</com.android.systemui.qs.QSPanel>
</com.android.systemui.qs.NonInterceptingScrollView>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index f8812ba..1d93f5d 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2016 The Android Open Source Project
+ Copyright (C) 2021 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.
@@ -14,84 +14,38 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.systemui.qs.tileimpl.ButtonRelativeLayout
+<com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingTop="12dp">
- <LinearLayout
- android:id="@+id/label_group"
- android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:layout_marginStart="@dimen/qs_label_container_margin"
+ android:layout_marginEnd="0dp"
+ android:layout_gravity="center_vertical | start">
+
+ <TextView
+ android:id="@+id/tile_label"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:gravity="center"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="horizontal">
- <Space
- android:id="@+id/expand_space"
- android:layout_width="22dp"
- android:layout_height="0dp"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/tile_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:clickable="false"
- android:padding="0dp"
- android:gravity="center"
- android:ellipsize="marquee"
- android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
-
- <ImageView android:id="@+id/restricted_padlock"
- android:layout_width="@dimen/qs_tile_text_size"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/qs_tile_text_size"
- android:src="@drawable/ic_info"
- android:layout_marginLeft="@dimen/restricted_padlock_pading"
- android:scaleType="centerInside"
- android:visibility="gone" />
-
- <ImageView
- android:id="@+id/expand_indicator"
- android:layout_marginStart="4dp"
- android:layout_width="18dp"
- android:layout_height="match_parent"
- android:src="@drawable/qs_dual_tile_caret"
- android:tint="?android:attr/textColorPrimary"
- android:visibility="gone" />
- </LinearLayout>
+ android:gravity="start"
+ android:textDirection="locale"
+ android:ellipsize="marquee"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
<TextView
android:id="@+id/app_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignStart="@id/label_group"
- android:layout_alignEnd="@id/label_group"
- android:layout_below="@id/label_group"
- android:clickable="false"
+ android:gravity="start"
+ android:textDirection="locale"
android:ellipsize="marquee"
android:singleLine="true"
- android:padding="0dp"
android:visibility="gone"
- android:gravity="center"
android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
android:textColor="?android:attr/textColorSecondary"/>
- <View
- android:id="@+id/underline"
- android:layout_width="30dp"
- android:layout_height="1dp"
- android:layout_marginTop="2dp"
- android:layout_alignStart="@id/label_group"
- android:layout_alignEnd="@id/label_group"
- android:layout_below="@id/label_group"
- android:visibility="gone"
- android:alpha="?android:attr/disabledAlpha"
- android:background="?android:attr/colorForeground"/>
-
-</com.android.systemui.qs.tileimpl.ButtonRelativeLayout>
+</com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
new file mode 100644
index 0000000..9f9af9d
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/qs_label_container_margin"
+ android:layout_gravity="center_vertical | end"
+>
+ <ImageView
+ android:id="@+id/customDrawable"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/qs_icon_size"
+ android:layout_marginEnd="@dimen/qs_drawable_end_margin"
+ android:adjustViewBounds="true"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/chevron"
+ android:layout_width="@dimen/qs_icon_size"
+ android:layout_height="@dimen/qs_icon_size"
+ android:src="@*android:drawable/ic_chevron_end"
+ android:visibility="gone"
+ android:importantForAccessibility="no"
+ />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index d8bb7e6..f0229a6 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -36,19 +36,16 @@
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
-
- <View
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="1"
- />
+ android:textAppearance="@style/TextAppearance.QS.Status" />
<include layout="@layout/qs_carrier_group"
android:id="@+id/carrier_group"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="match_parent"
+ android:layout_weight="1"
android:minHeight="48dp"
+ android:minWidth="48dp"
+ android:layout_marginStart="8dp"
android:layout_gravity="end|center_vertical"
android:focusable="false"/>
diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
index de65fa0..ce7f827 100644
--- a/packages/SystemUI/res/layout/quick_settings_security_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
@@ -23,7 +23,7 @@
android:padding="@dimen/qs_footer_padding"
android:gravity="center_vertical"
android:layout_gravity="center_vertical|center_horizontal"
- android:layout_marginVertical="@dimen/qs_security_footer_vertical_margin"
+ android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
android:background="@drawable/qs_security_footer_background"
systemui:singleLineHeight="@dimen/qs_security_footer_single_line_height"
systemui:textViewId="@id/footer_text"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index bb54099..d8f0742 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -52,7 +52,6 @@
android:layout_below="@id/quick_qs_status_icons"
android:layout_marginTop="8dp"
android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
- android:accessibilityTraversalBefore="@id/expand_indicator"
android:clipChildren="false"
android:clipToPadding="false"
android:focusable="true"
diff --git a/packages/SystemUI/res/layout/wallet_card_view.xml b/packages/SystemUI/res/layout/wallet_card_view.xml
index 5fd556d..b5a66010 100644
--- a/packages/SystemUI/res/layout/wallet_card_view.xml
+++ b/packages/SystemUI/res/layout/wallet_card_view.xml
@@ -24,6 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="@dimen/card_margin"
+ android:layout_marginBottom="@dimen/card_margin"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="@android:color/transparent"
app:cardElevation="12dp">
@@ -32,16 +33,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
+ android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="fitXY"/>
- <ImageView
- android:id="@+id/add_card_logo"
- android:layout_width="28dp"
- android:layout_height="28dp"
- android:layout_gravity="center"
- android:drawable="@drawable/ic_qs_plus"
- android:contentDescription="@null"
- android:scaleType="fitCenter"
- android:visibility="gone"/>
</com.android.systemui.wallet.ui.WalletCardView>
</FrameLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 46e7d71..96809dc 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -32,7 +32,7 @@
<dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen>
<dimen name="qs_footer_padding">14dp</dimen>
- <dimen name="qs_security_footer_vertical_margin">0dp</dimen>
+ <dimen name="qs_footers_margin_bottom">0dp</dimen>
<dimen name="qs_security_footer_background_inset">12dp</dimen>
<dimen name="qs_security_footer_corner_radius">28dp</dimen>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 8f88950..c473229 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -95,9 +95,6 @@
<!-- Color of background circle of user avatars in quick settings user switcher -->
<color name="qs_user_switcher_avatar_background">#3C4043</color>
- <!-- Colors for privacy dialog. These should be changed to the new palette -->
- <color name="privacy_circle">#81C995</color> <!-- g300 -->
-
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index ee25a10..08a2e19 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -255,7 +255,6 @@
<!-- media -->
<color name="media_disabled">#80ffffff</color>
<color name="media_seamless_border">?android:attr/colorAccent</color>
- <color name="media_divider">#1d000000</color>
<!-- controls -->
<color name="control_primary_text">#E6FFFFFF</color>
@@ -280,7 +279,7 @@
<color name="screenrecord_status_color">#E94235</color>
- <color name="privacy_circle">#5BB974</color> <!-- g400 -->
+ <color name="privacy_chip_background">#3ddc84</color>
<!-- Accessibility floating menu -->
<color name="accessibility_floating_menu_background">#CCFFFFFF</color> <!-- 80% -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 073913d..e3139b8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -555,7 +555,9 @@
<dimen name="qs_icon_size">20dp</dimen>
<dimen name="qs_label_container_margin">10dp</dimen>
<dimen name="qs_quick_tile_size">60dp</dimen>
- <dimen name="qs_quick_tile_padding">12dp</dimen>
+ <dimen name="qs_tile_padding">12dp</dimen>
+ <dimen name="qs_tile_start_padding">16dp</dimen>
+ <dimen name="qs_drawable_end_margin">4dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
@@ -564,8 +566,6 @@
Scaled @dimen/qs_page_indicator-width by .4f.
-->
<dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
- <dimen name="qs_tile_side_label_padding">12dp</dimen>
- <dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">14sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
@@ -616,7 +616,7 @@
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
<dimen name="qs_security_footer_single_line_height">48dp</dimen>
- <dimen name="qs_security_footer_vertical_margin">8dp</dimen>
+ <dimen name="qs_footers_margin_bottom">8dp</dimen>
<dimen name="qs_security_footer_background_inset">0dp</dimen>
<dimen name="qs_security_footer_corner_radius">28dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3480d10..4da2e33 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -690,11 +690,11 @@
<!-- Announcement made when the screen stopped casting (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_casting_turned_off">Screen casting stopped.</string>
<!-- Content description of the work mode title in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_off">Work mode off.</string>
+ <string name="accessibility_quick_settings_work_mode_off">Work mode paused.</string>
<!-- Content description of the work mode title in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_on">Work mode on.</string>
<!-- Announcement made when the work mode changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned off.</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned paused.</string>
<!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
<!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -954,6 +954,8 @@
off. "Work profile" means a separate profile on a user's phone that's specifically for their
work apps and managed by their company. "Work" is used as an adjective. [CHAR LIMIT=NONE] -->
<string name="quick_settings_work_mode_label">Work profile</string>
+ <!-- QuickSettings: Secondary label for work mode tile when it's off. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_work_mode_paused">Paused</string>
<!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
<string name="quick_settings_night_display_label">Night Light</string>
<!-- QuickSettings: Secondary text for when the Night Light will be enabled at sunset. [CHAR LIMIT=20] -->
@@ -2187,6 +2189,9 @@
<!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
<string name="tile_unavailable">Unavailable</string>
+ <!-- The tile in quick settings is disabled by a device administration policy [CHAR LIMIT=32] -->
+ <string name="tile_disabled">Disabled</string>
+
<!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
<string name="nav_bar">Navigation bar</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index aa1f954..7db9dec 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -226,13 +226,21 @@
<item name="android:textSize">@dimen/qs_tile_text_size</item>
</style>
- <style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary">
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">@color/dark_mode_qs_icon_color_single_tone</item>
+ <style name="TextAppearance.QS.Status">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:letterSpacing">0.01</item>
+ <item name="android:lineHeight">20sp</item>
+ </style>
+
+ <style name="TextAppearance.QS.Status.NoCarrierText">
+ <item name="android:textColor">?android:attr/textColorTertiary</item>
</style>
<style name="TextAppearance.QS.Build">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:textSize">14sp</item>
</style>
@@ -389,7 +397,6 @@
<style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:colorError">@*android:color/error_color_material_dark</item>
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index e92cae4..dfe035d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -45,7 +45,6 @@
import com.android.keyguard.clock.ClockManager;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
@@ -69,6 +68,7 @@
import com.android.systemui.util.settings.SecureSettings;
import java.util.Locale;
+import java.util.Optional;
import java.util.TimeZone;
import java.util.concurrent.Executor;
@@ -89,7 +89,6 @@
private final Executor mUiExecutor;
private final BatteryController mBatteryController;
private final FeatureFlags mFeatureFlags;
- private final SystemUIFactory mSystemUIFactory;
/**
* Clock for both small and large sizes
@@ -152,6 +151,7 @@
// If set, will replace keyguard_status_area
private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
+ private Optional<BcSmartspaceDataPlugin> mSmartspacePlugin;
@Inject
public KeyguardClockSwitchController(
@@ -165,14 +165,14 @@
@Main Executor uiExecutor,
BatteryController batteryController,
ConfigurationController configurationController,
- SystemUIFactory systemUIFactory,
ActivityStarter activityStarter,
FalsingManager falsingManager,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardBypassController bypassController,
@Main Handler handler,
UserTracker userTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ Optional<BcSmartspaceDataPlugin> smartspacePlugin) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -184,7 +184,6 @@
mUiExecutor = uiExecutor;
mBatteryController = batteryController;
mConfigurationController = configurationController;
- mSystemUIFactory = systemUIFactory;
mActivityStarter = activityStarter;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -192,6 +191,7 @@
mHandler = handler;
mUserTracker = userTracker;
mSecureSettings = secureSettings;
+ mSmartspacePlugin = smartspacePlugin;
}
/**
@@ -237,8 +237,8 @@
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
- BcSmartspaceDataPlugin smartspaceDataPlugin = mSystemUIFactory.getSmartspaceDataProvider();
- if (mFeatureFlags.isSmartspaceEnabled() && smartspaceDataPlugin != null) {
+ if (mFeatureFlags.isSmartspaceEnabled() && mSmartspacePlugin.isPresent()) {
+ BcSmartspaceDataPlugin smartspaceDataPlugin = mSmartspacePlugin.get();
View ksa = mView.findViewById(R.id.keyguard_status_area);
int ksaIndex = mView.indexOfChild(ksa);
ksa.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index af064e1..cc167b9 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -29,7 +29,6 @@
import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider;
-import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
import com.android.wm.shell.transition.Transitions;
@@ -210,8 +209,4 @@
AssetManager am, String modelName) {
return new BackGestureTfClassifierProvider();
}
-
- public BcSmartspaceDataPlugin getSmartspaceDataProvider() {
- return null;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index 195d006..b7344fb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -45,7 +45,7 @@
@NonNull final StatusBar mStatusBar;
@NonNull final DumpManager mDumpManger;
- private boolean mNotificationShadeExpanded;
+ boolean mNotificationShadeExpanded;
protected UdfpsAnimationViewController(
T view,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 37ea251..52576a7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -40,12 +40,14 @@
public class UdfpsKeyguardView extends UdfpsAnimationView {
private final UdfpsKeyguardDrawable mFingerprintDrawable;
private ImageView mFingerprintView;
- private int mWallpaperTexColor;
+ private int mWallpaperTextColor;
private int mStatusBarState;
// used when highlighting fp icon:
private int mTextColorPrimary;
private ImageView mBgProtection;
+ boolean mUdfpsRequested;
+ int mUdfpsRequestedColor;
private AnimatorSet mAnimatorSet;
private int mAlpha; // 0-255
@@ -63,10 +65,11 @@
mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
- mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext,
+ mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColorAccent);
mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorPrimary);
+ mUdfpsRequested = false;
}
@Override
@@ -90,11 +93,31 @@
return true;
}
+ void requestUdfps(boolean request, int color) {
+ if (request) {
+ mUdfpsRequestedColor = color;
+ } else {
+ mUdfpsRequestedColor = -1;
+ }
+ mUdfpsRequested = request;
+ updateColor();
+ }
+
void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
- if (!isShadeLocked()) {
- mFingerprintView.setAlpha(1f);
- mFingerprintDrawable.setLockScreenColor(mWallpaperTexColor);
+ updateColor();
+ }
+
+ void updateColor() {
+ mFingerprintView.setAlpha(1f);
+ mFingerprintDrawable.setLockScreenColor(getColor());
+ }
+
+ private int getColor() {
+ if (mUdfpsRequested && mUdfpsRequestedColor != -1) {
+ return mUdfpsRequestedColor;
+ } else {
+ return mWallpaperTextColor;
}
}
@@ -142,7 +165,7 @@
} else {
// update icon color
fpIconAnim = new ValueAnimator();
- fpIconAnim.setIntValues(mWallpaperTexColor, mTextColorPrimary);
+ fpIconAnim.setIntValues(getColor(), mTextColorPrimary);
fpIconAnim.setEvaluator(new ArgbEvaluator());
fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor(
(Integer) valueAnimator.getAnimatedValue()));
@@ -170,10 +193,6 @@
return mStatusBarState == StatusBarState.SHADE_LOCKED;
}
- boolean isHome() {
- return mStatusBarState == StatusBarState.SHADE;
- }
-
/**
* Animates out the bg protection circle behind the fp icon to unhighlight the icon.
*/
@@ -193,7 +212,7 @@
} else {
// update icon color
fpIconAnim = new ValueAnimator();
- fpIconAnim.setIntValues(mTextColorPrimary, mWallpaperTexColor);
+ fpIconAnim.setIntValues(mTextColorPrimary, getColor());
fpIconAnim.setEvaluator(new ArgbEvaluator());
fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor(
(Integer) valueAnimator.getAnimatedValue()));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 4d2f809..33d0d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -57,6 +57,7 @@
@Nullable private Runnable mCancelRunnable;
private boolean mShowingUdfpsBouncer;
+ private boolean mUdfpsRequested;
private boolean mQsExpanded;
private boolean mFaceDetectRunning;
private boolean mHintShown;
@@ -70,6 +71,7 @@
* {@link KeyguardBouncer#EXPANSION_HIDDEN} (1f)
*/
private float mInputBouncerHiddenAmount;
+ private boolean mIsBouncerVisible;
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@@ -105,6 +107,7 @@
mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
mStatusBarStateController.addCallback(mStateListener);
+ mUdfpsRequested = false;
mStatusBarState = mStatusBarStateController.getState();
mQsExpanded = mKeyguardViewManager.isQsExpanded();
mKeyguardIsVisible = mKeyguardUpdateMonitor.isKeyguardVisible();
@@ -113,6 +116,7 @@
updatePauseAuth();
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
}
@Override
@@ -137,11 +141,15 @@
pw.println("mShowingUdfpsBouncer=" + mShowingUdfpsBouncer);
pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
pw.println("mTransitioningFromHomeToKeyguard=" + mTransitioningFromHome);
- pw.println("mStatusBarState" + StatusBarState.toShortString(mStatusBarState));
+ pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState));
pw.println("mQsExpanded=" + mQsExpanded);
pw.println("mKeyguardVisible=" + mKeyguardIsVisible);
+ pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
pw.println("mAlpha=" + mView.getAlpha());
+ pw.println("mUdfpsRequested=" + mUdfpsRequested);
+ pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
+ pw.println("mView.mUdfpsRequestedColor=" + mView.mUdfpsRequestedColor);
}
/**
@@ -173,6 +181,12 @@
return false;
}
+ if (mUdfpsRequested && !mNotificationShadeExpanded
+ && (!mIsBouncerVisible
+ || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)) {
+ return false;
+ }
+
if (mStatusBarState != KEYGUARD) {
return true;
}
@@ -333,6 +347,13 @@
}
@Override
+ public void requestUdfps(boolean request, int color) {
+ mUdfpsRequested = request;
+ mView.requestUdfps(request, color);
+ updatePauseAuth();
+ }
+
+ @Override
public boolean isAnimating() {
return mView.isAnimating();
}
@@ -356,6 +377,12 @@
}
@Override
+ public void onBouncerVisibilityChanged() {
+ mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
+ updatePauseAuth();
+ }
+
+ @Override
public void dump(PrintWriter pw) {
pw.println(getTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 126724c..7fa48d4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -38,6 +38,7 @@
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.dagger.PowerModule;
import com.android.systemui.recents.Recents;
@@ -151,6 +152,9 @@
abstract HeadsUpManager optionalHeadsUpManager();
@BindsOptionalOf
+ abstract BcSmartspaceDataPlugin optionalBcSmartspaceDataPlugin();
+
+ @BindsOptionalOf
abstract Recents optionalRecents();
@BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index bd3b899..30429fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -38,7 +38,6 @@
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.style.StyleSpan;
-import android.util.Log;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -53,8 +52,6 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIAppComponentFactory;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
@@ -65,8 +62,6 @@
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -318,25 +313,7 @@
mPendingIntent = PendingIntent.getActivity(getContext(), 0,
new Intent(getContext(), KeyguardSliceProvider.class),
PendingIntent.FLAG_IMMUTABLE);
- try {
- //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood.
- mMediaManager.addCallback(this);
- } catch (NullPointerException e) {
- // We are sometimes failing to set the media manager. Why?
- Log.w(TAG, "Failed to setup mMediaManager. Trying again.");
- SysUIComponent rootComponent = SystemUIFactory.getInstance().getSysUIComponent();
- try {
- Method injectMethod = rootComponent.getClass()
- .getMethod("inject", getClass());
- injectMethod.invoke(rootComponent, this);
- Log.w(TAG, "mMediaManager is now: " + mMediaManager);
- } catch (NoSuchMethodException ex) {
- Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex);
- } catch (IllegalAccessException | InvocationTargetException ex) {
- Log.e(TAG, "Failed to call inject", ex);
- }
- throw e;
- }
+ mMediaManager.addCallback(this);
mStatusBarStateController.addCallback(this);
mNextAlarmController.addCallback(this);
mZenModeController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index be96ba8..1010b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -398,14 +398,8 @@
// Fade in the security footer and the divider as we reach the final position
builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1);
- if (mQsPanelController.getDivider() != null) {
- builder.addFloat(mQsPanelController.getDivider(), "alpha", 0, 1);
- }
mAllPagesDelayedAnimator = builder.build();
mAllViews.add(mSecurityFooter.getView());
- if (mQsPanelController.getDivider() != null) {
- mAllViews.add(mQsPanelController.getDivider());
- }
translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
mTranslationYAnimator = translationYBuilder.build();
if (mQQSTileHeightAnimator != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 40967ed..57438d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -45,7 +45,6 @@
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R;
-import com.android.systemui.R.dimen;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
@@ -140,7 +139,7 @@
void updateAnimator(int width, int numTiles) {
int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- - mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
+ - mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding);
int remaining = (width - numTiles * size) / (numTiles - 1);
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
@@ -167,6 +166,9 @@
private void updateResources() {
updateFooterAnimator();
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+ lp.bottomMargin = getResources().getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
+ setLayoutParams(lp);
mTunerIconTranslation = mContext.getResources()
.getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation);
mTunerIcon.setTranslationX(isLayoutRtl() ? -mTunerIconTranslation : mTunerIconTranslation);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 4e16b74..7062e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -84,8 +84,6 @@
@Nullable
protected View mFooter;
- @Nullable
- protected View mDivider;
@Nullable
private ViewGroup mHeaderContainer;
@@ -327,7 +325,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
mFooter = findViewById(R.id.qs_footer);
- mDivider = findViewById(R.id.divider);
}
private void updateHorizontalLinearLayoutMargins() {
@@ -602,11 +599,6 @@
return mTileLayout;
}
- @Nullable
- public View getDivider() {
- return mDivider;
- }
-
/** */
public void setContentMargins(int startMargin, int endMargin, ViewGroup mediaHostView) {
// Only some views actually want this content padding, others want to go all the way
@@ -614,12 +606,6 @@
mContentMarginStart = startMargin;
mContentMarginEnd = endMargin;
updateMediaHostContentMargins(mediaHostView);
- updateDividerMargin();
- }
-
- private void updateDividerMargin() {
- if (mDivider == null) return;
- updateMargins(mDivider, mContentMarginStart, mContentMarginEnd);
}
/**
@@ -712,7 +698,6 @@
}
private void updateMargins(ViewGroup mediaHostView) {
- updateDividerMargin();
updateMediaHostContentMargins(mediaHostView);
updateHorizontalLinearLayoutMargins();
updatePadding();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index fff3d1f..ac92d4f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -284,10 +284,6 @@
return mView.getBrightnessView();
}
- public View getDivider() {
- return mView.getDivider();
- }
-
/** */
public void setPageListener(PagedTileLayout.PageListener listener) {
mView.setPageListener(listener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index e40f293..170785c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -22,7 +22,6 @@
import android.content.ComponentName;
import android.content.res.Configuration;
import android.metrics.LogMaker;
-import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
@@ -294,13 +293,6 @@
boolean switchTileLayout(boolean force) {
/** Whether or not the QuickQSPanel currently contains a media player. */
boolean horizontal = shouldUseHorizontalLayout();
- if (mView.getDivider() != null) {
- if (!horizontal && mUsingMediaPlayer && mMediaHost.getVisible()) {
- mView.getDivider().setVisibility(View.VISIBLE);
- } else {
- mView.getDivider().setVisibility(View.GONE);
- }
- }
if (horizontal != mUsingHorizontalLayout || force) {
mUsingHorizontalLayout = horizontal;
for (QSPanelControllerBase.TileRecord record : mRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index baf781d..04e32a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -128,11 +128,10 @@
int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
mRootView.setPaddingRelative(padding, padding, padding, padding);
- int verticalMargin = r.getDimensionPixelSize(R.dimen.qs_security_footer_vertical_margin);
+ int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
- lp.topMargin = verticalMargin;
- lp.bottomMargin = verticalMargin;
+ lp.bottomMargin = bottomMargin;
lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
? MATCH_PARENT : WRAP_CONTENT;
mRootView.setLayoutParams(lp);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 63733b3..c3458fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -285,8 +285,6 @@
if (record.tileView.getVisibility() == GONE) continue;
previousView = record.tileView.updateAccessibilityOrder(previousView);
}
- mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore(
- R.id.expand_indicator);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 5afe1c8..c49e054 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -43,6 +43,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.util.CarrierConfigTracker;
import java.util.function.Consumer;
@@ -71,6 +72,7 @@
private int[] mLastSignalLevel = new int[SIM_SLOTS];
private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
private final boolean mProviderModel;
+ private final CarrierConfigTracker mCarrierConfigTracker;
private final NetworkController.SignalCallback mSignalCallback =
new NetworkController.SignalCallback() {
@@ -112,6 +114,10 @@
Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
return;
}
+
+ boolean displayCallStrengthIcon =
+ mCarrierConfigTracker.getCallStrengthConfig(subId);
+
if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
if (statusIcon.visible) {
mInfos[slotIndex] = new CellSignalState(true,
@@ -119,9 +125,14 @@
} else {
// Whenever the no Calling & SMS state is cleared, switched to the last
// known call strength icon.
- mInfos[slotIndex] = new CellSignalState(
- true, mLastSignalLevel[slotIndex],
- mLastSignalLevelDescription[slotIndex], "", false);
+ if (displayCallStrengthIcon) {
+ mInfos[slotIndex] = new CellSignalState(
+ true, mLastSignalLevel[slotIndex],
+ mLastSignalLevelDescription[slotIndex], "", false);
+ } else {
+ mInfos[slotIndex] = new CellSignalState(
+ true, R.drawable.ic_qs_sim_card, "", "", false);
+ }
}
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
} else {
@@ -131,8 +142,13 @@
// shown.
if (mInfos[slotIndex].mobileSignalIconId
!= R.drawable.ic_qs_no_calling_sms) {
- mInfos[slotIndex] = new CellSignalState(true, statusIcon.icon,
- statusIcon.contentDescription, "", false);
+ if (displayCallStrengthIcon) {
+ mInfos[slotIndex] = new CellSignalState(true, statusIcon.icon,
+ statusIcon.contentDescription, "", false);
+ } else {
+ mInfos[slotIndex] = new CellSignalState(
+ true, R.drawable.ic_qs_sim_card, "", "", false);
+ }
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
}
@@ -165,7 +181,8 @@
private QSCarrierGroupController(QSCarrierGroup view, ActivityStarter activityStarter,
@Background Handler bgHandler, @Main Looper mainLooper,
NetworkController networkController,
- CarrierTextManager.Builder carrierTextManagerBuilder, Context context) {
+ CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
+ CarrierConfigTracker carrierConfigTracker) {
if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
mProviderModel = true;
} else {
@@ -178,7 +195,7 @@
.setShowAirplaneMode(false)
.setShowMissingSim(false)
.build();
-
+ mCarrierConfigTracker = carrierConfigTracker;
View.OnClickListener onClickListener = v -> {
if (!v.isVisibleToUser()) {
return;
@@ -228,6 +245,17 @@
return SubscriptionManager.getSlotIndex(subscriptionId);
}
+ private boolean isSingleCarrier() {
+ int carrierCount = 0;
+ for (int i = 0; i < SIM_SLOTS; i++) {
+
+ if (mInfos[i].visible) {
+ carrierCount++;
+ }
+ }
+ return carrierCount == 1;
+ }
+
public void setListening(boolean listening) {
if (listening == mListening) {
return;
@@ -257,6 +285,15 @@
return;
}
+ if (isSingleCarrier()) {
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ if (mInfos[i].visible
+ && mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
+ mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
+ }
+ }
+ }
+
for (int i = 0; i < SIM_SLOTS; i++) {
mCarrierGroups[i].updateState(mInfos[i]);
}
@@ -363,17 +400,20 @@
private final NetworkController mNetworkController;
private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
private final Context mContext;
+ private final CarrierConfigTracker mCarrierConfigTracker;
@Inject
public Builder(ActivityStarter activityStarter, @Background Handler handler,
@Main Looper looper, NetworkController networkController,
- CarrierTextManager.Builder carrierTextControllerBuilder, Context context) {
+ CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
+ CarrierConfigTracker carrierConfigTracker) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
mNetworkController = networkController;
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
mContext = context;
+ mCarrierConfigTracker = carrierConfigTracker;
}
public Builder setQSCarrierGroup(QSCarrierGroup view) {
@@ -383,7 +423,8 @@
public QSCarrierGroupController build() {
return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
- mNetworkController, mCarrierTextControllerBuilder, mContext);
+ mNetworkController, mCarrierTextControllerBuilder, mContext,
+ mCarrierConfigTracker);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
deleted file mode 100644
index ce8f6c1..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs.customize;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.tileimpl.QSTileView;
-
-public class CustomizeTileView extends QSTileView implements TileAdapter.CustomizeView {
- private boolean mShowAppLabel;
-
- public CustomizeTileView(Context context, QSIconView icon) {
- super(context, icon);
- }
-
- @Override
- public void setShowAppLabel(boolean showAppLabel) {
- mShowAppLabel = showAppLabel;
- mSecondLine.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
- mLabel.setSingleLine(showAppLabel);
- }
-
- @Override
- protected void handleStateChanged(QSTile.State state) {
- super.handleStateChanged(state);
- mSecondLine.setVisibility(mShowAppLabel ? View.VISIBLE : View.GONE);
- }
-
- @Override
- protected boolean animationsEnabled() {
- return false;
- }
-
- @Override
- public boolean isLongClickable() {
- return false;
- }
-
- @Override
- public void changeState(QSTile.State state) {
- handleStateChanged(state);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
new file mode 100644
index 0000000..a316e6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.customize
+
+import android.content.Context
+import android.text.TextUtils
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileViewImpl
+
+/**
+ * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
+ */
+class CustomizeTileView(
+ context: Context,
+ icon: QSIconView
+) : QSTileViewImpl(context, icon, collapsed = false) {
+
+ var showAppLabel = false
+ set(value) {
+ field = value
+ secondaryLabel.visibility = getVisibilityState(secondaryLabel.text)
+ }
+
+ var showSideView = true
+ set(value) {
+ field = value
+ if (!showSideView) sideView.visibility = GONE
+ }
+
+ override fun handleStateChanged(state: QSTile.State) {
+ super.handleStateChanged(state)
+ showRippleEffect = false
+ secondaryLabel.visibility = getVisibilityState(state.secondaryLabel)
+ if (!showSideView) sideView.visibility = GONE
+ }
+
+ private fun getVisibilityState(text: CharSequence?): Int {
+ return if (showAppLabel && !TextUtils.isEmpty(text)) {
+ VISIBLE
+ } else {
+ GONE
+ }
+ }
+
+ override fun animationsEnabled(): Boolean {
+ return false
+ }
+
+ override fun isLongClickable(): Boolean {
+ return false
+ }
+
+ fun changeState(state: QSTile.State) {
+ handleStateChanged(state)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
deleted file mode 100644
index 7977b49..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.systemui.qs.customize
-
-import android.content.Context
-import android.view.View
-import com.android.systemui.plugins.qs.QSIconView
-import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tileimpl.QSTileViewHorizontal
-
-/**
- * Class for displaying tiles in [QSCustomizer] with the new design (labels on the side).
- *
- * This is a class parallel to [CustomizeTileView], but inheriting from [QSTileViewHorizontal].
- */
-class CustomizeTileViewHorizontal(
- context: Context,
- icon: QSIconView
-) : QSTileViewHorizontal(context, icon, collapsed = false),
- TileAdapter.CustomizeView {
-
- private var showAppLabel = false
-
- override fun setShowAppLabel(showAppLabel: Boolean) {
- this.showAppLabel = showAppLabel
- mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
- mLabel.isSingleLine = showAppLabel
- }
-
- override fun handleStateChanged(state: QSTile.State) {
- super.handleStateChanged(state)
- mShowRippleEffect = false
- mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
- }
-
- override fun animationsEnabled(): Boolean {
- return false
- }
-
- override fun isLongClickable(): Boolean {
- return false
- }
-
- override fun changeState(state: QSTile.State) {
- handleStateChanged(state)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 5080533..fe0e56c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -43,7 +43,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.customize.TileAdapter.Holder;
@@ -53,7 +52,7 @@
import com.android.systemui.qs.dagger.QSThemedContext;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.QSTileView;
+import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import java.util.ArrayList;
import java.util.List;
@@ -280,7 +279,7 @@
}
FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
false);
- View view = new CustomizeTileViewHorizontal(context, new QSIconViewImpl(context));
+ View view = new CustomizeTileView(context, new QSIconViewImpl(context));
frame.addView(view);
return new Holder(frame);
}
@@ -367,6 +366,8 @@
// The holder has a tileView, therefore this call is not null
holder.getTileAsCustomizeView().changeState(info.state);
holder.getTileAsCustomizeView().setShowAppLabel(position > mEditIndex && !info.isSystem);
+ // Don't show the side view for third party tiles, as we don't have the actual state.
+ holder.getTileAsCustomizeView().setShowSideView(position < mEditIndex || info.isSystem);
holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
holder.mTileView.setClickable(true);
holder.mTileView.setOnClickListener(null);
@@ -545,15 +546,12 @@
}
public class Holder extends ViewHolder {
- private QSTileView mTileView;
+ private QSTileViewImpl mTileView;
public Holder(View itemView) {
super(itemView);
if (itemView instanceof FrameLayout) {
- mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
- if (mTileView instanceof CustomizeTileView) {
- mTileView.setBackground(null);
- }
+ mTileView = (QSTileViewImpl) ((FrameLayout) itemView).getChildAt(0);
mTileView.getIcon().disableAnimation();
mTileView.setTag(this);
ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate);
@@ -561,8 +559,8 @@
}
@Nullable
- public CustomizeView getTileAsCustomizeView() {
- return (CustomizeView) mTileView;
+ public CustomizeTileView getTileAsCustomizeView() {
+ return (CustomizeTileView) mTileView;
}
public void clearDrag() {
@@ -570,8 +568,8 @@
if (mTileView instanceof CustomizeTileView) {
mTileView.findViewById(R.id.tile_label).clearAnimation();
mTileView.findViewById(R.id.tile_label).setAlpha(1);
- mTileView.getAppLabel().clearAnimation();
- mTileView.getAppLabel().setAlpha(.6f);
+ mTileView.getSecondaryLabel().clearAnimation();
+ mTileView.getSecondaryLabel().setAlpha(.6f);
}
}
@@ -584,7 +582,7 @@
mTileView.findViewById(R.id.tile_label).animate()
.setDuration(DRAG_LENGTH)
.alpha(0);
- mTileView.getAppLabel().animate()
+ mTileView.getSecondaryLabel().animate()
.setDuration(DRAG_LENGTH)
.alpha(0);
}
@@ -599,7 +597,7 @@
mTileView.findViewById(R.id.tile_label).animate()
.setDuration(DRAG_LENGTH)
.alpha(1);
- mTileView.getAppLabel().animate()
+ mTileView.getSecondaryLabel().animate()
.setDuration(DRAG_LENGTH)
.alpha(.6f);
}
@@ -765,7 +763,7 @@
int position = mCurrentDrag.getAdapterPosition();
if (position == RecyclerView.NO_POSITION) return;
TileInfo info = mTiles.get(position);
- ((CustomizeView) mCurrentDrag.mTileView).setShowAppLabel(
+ ((CustomizeTileView) mCurrentDrag.mTileView).setShowAppLabel(
position > mEditIndex && !info.isSystem);
mCurrentDrag.stopDrag();
mCurrentDrag = null;
@@ -825,9 +823,4 @@
public void onSwiped(ViewHolder viewHolder, int direction) {
}
};
-
- interface CustomizeView {
- void setShowAppLabel(boolean showAppLabel);
- void changeState(@NonNull QSTile.State state);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
deleted file mode 100644
index 962537a..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs.tileimpl;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.RelativeLayout;
-
-/**
- * Used for QS tile labels
- */
-public class ButtonRelativeLayout extends RelativeLayout {
-
- private View mIgnoredView;
-
- public ButtonRelativeLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public CharSequence getAccessibilityClassName() {
- return Button.class.getName();
- }
-
- /**
- * Set a view to be ignored for measure.
- *
- * The view will be measured and laid out, but its size will be subtracted from the total size
- * of this view. It assumes that this view only contributes vertical height.
- */
- public void setIgnoredView(View view) {
- if (mIgnoredView == null || mIgnoredView.getParent() == this) {
- mIgnoredView = view;
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mIgnoredView != null && mIgnoredView.getVisibility() != GONE) {
- int height = mIgnoredView.getMeasuredHeight();
- MarginLayoutParams lp = (MarginLayoutParams) mIgnoredView.getLayoutParams();
- height = height - lp.bottomMargin - lp.topMargin;
- setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - height);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
new file mode 100644
index 0000000..7055760
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/**
+ * [LinearLayout] that can ignore the last child for measuring.
+ *
+ * The view is measured as regularlt, then if [ignoreLastView] is true:
+ * * In [LinearLayout.VERTICAL] orientation, the height of the last view is subtracted from the
+ * final measured height.
+ * * In [LinearLayout.HORIZONTAL] orientation, the width of the last view is subtracted from the
+ * final measured width.
+ *
+ * This allows to measure the view and position it where it should, without it amounting to the
+ * total size (only in the direction of layout).
+ */
+class IgnorableChildLinearLayout @JvmOverloads constructor(
+ context: Context,
+ attributeSet: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : LinearLayout(context, attributeSet, defStyleAttr, defStyleRes) {
+
+ var ignoreLastView = false
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ if (ignoreLastView && childCount > 0) {
+ val lastView = getChildAt(childCount - 1)
+ val lp = lastView.layoutParams as MarginLayoutParams
+ if (orientation == VERTICAL) {
+ val height = lastView.measuredHeight + lp.bottomMargin + lp.topMargin
+ setMeasuredDimension(measuredWidth, measuredHeight - height)
+ } else {
+ val width = lastView.measuredWidth + lp.leftMargin + lp.rightMargin
+ setMeasuredDimension(measuredWidth - width, measuredHeight)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 3437dd5..8f7c493 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -243,6 +243,6 @@
@Override
public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
QSIconView icon = tile.createTileView(context);
- return new QSTileViewHorizontal(context, icon, collapsedView);
+ return new QSTileViewImpl(context, icon, collapsedView);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 31526bf..8280f98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -14,22 +14,24 @@
package com.android.systemui.qs.tileimpl;
-import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Animatable2.AnimationCallback;
import android.graphics.drawable.Drawable;
+import android.service.quicksettings.Tile;
+import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
@@ -43,7 +45,7 @@
public static final long QS_ANIM_LENGTH = 350;
protected final View mIcon;
- protected final int mIconSizePx;
+ protected int mIconSizePx;
private boolean mAnimationEnabled = true;
private int mState = -1;
private int mTint;
@@ -53,12 +55,18 @@
super(context);
final Resources res = context.getResources();
- mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_tile_icon_size);
+ mIconSizePx = res.getDimensionPixelSize(R.dimen.qs_icon_size);
mIcon = createIcon();
addView(mIcon);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mIconSizePx = getContext().getResources().getDimensionPixelSize(R.dimen.qs_icon_size);
+ }
+
public void disableAnimation() {
mAnimationEnabled = false;
}
@@ -169,7 +177,7 @@
}
protected int getColor(int state) {
- return getColorForState(getContext(), state);
+ return getIconColorForState(getContext(), state);
}
private void animateGrayScale(int fromColor, int toColor, ImageView iv,
@@ -229,4 +237,21 @@
protected final void layout(View child, int left, int top) {
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
+
+ /**
+ * Color to tint the tile icon based on state
+ */
+ public static int getIconColorForState(Context context, int state) {
+ switch (state) {
+ case Tile.STATE_UNAVAILABLE:
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary);
+ case Tile.STATE_INACTIVE:
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
+ case Tile.STATE_ACTIVE:
+ return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
+ default:
+ Log.e("QSIconView", "Invalid state " + state);
+ return 0;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
deleted file mode 100644
index c973e8c..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.systemui.qs.tileimpl;
-
-import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION;
-
-import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.PathShape;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.service.quicksettings.Tile;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.PathParser;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.Switch;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.BooleanState;
-
-public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
-
- private static final String TAG = "QSTileBaseView";
- private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
- protected final Handler mHandler = new H();
- private final int[] mLocInScreen = new int[2];
- protected final FrameLayout mIconFrame;
- protected QSIconView mIcon;
- protected RippleDrawable mRipple;
- protected Drawable mTileBackground;
- private String mAccessibilityClass;
- private boolean mTileState;
- protected boolean mCollapsedView;
- protected boolean mShowRippleEffect = true;
- private float mStrokeWidthActive;
- private float mStrokeWidthInactive;
-
- protected final ImageView mBg;
- private final int mColorActive;
- private final int mColorInactive;
- private int mCircleColor;
- private int mBgSize;
-
- private static final int INVALID = -1;
- private CharSequence mStateDescriptionDeltas = null;
- private CharSequence mLastStateDescription;
- private int mLastState = INVALID;
-
- public QSTileBaseView(Context context, QSIconView icon) {
- this(context, icon, false);
- }
-
- public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
- super(context);
- // Default to Quick Tile padding, and QSTileView will specify its own padding.
- int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
- mIconFrame = new FrameLayout(context);
- mStrokeWidthActive = context.getResources()
- .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthActive);
- mStrokeWidthInactive = context.getResources()
- .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthInactive);
- int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
- addView(mIconFrame, new LayoutParams(size, size));
- mBg = new ImageView(getContext());
- Path path = new Path(PathParser.createPathFromPathData(
- context.getResources().getString(ICON_MASK_ID)));
- float pathSize = AdaptiveIconDrawable.MASK_SIZE;
- PathShape p = new PathShape(path, pathSize, pathSize);
- ShapeDrawable d = new ShapeDrawable(p);
- d.setTintList(ColorStateList.valueOf(Color.TRANSPARENT));
- float backgroundStrokeWidth = context.getResources()
- .getDimension(R.dimen.qs_tile_icon_background_stroke_width);
- if (backgroundStrokeWidth > 0) {
- d.getPaint().setStyle(Paint.Style.STROKE);
- d.getPaint().setStrokeWidth(backgroundStrokeWidth);
- }
- int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
- d.setIntrinsicHeight(bgSize);
- d.setIntrinsicWidth(bgSize);
- mBg.setImageDrawable(d);
- FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(bgSize, bgSize, Gravity.CENTER);
- mIconFrame.addView(mBg, lp);
- mBg.setLayoutParams(lp);
- mIcon = icon;
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
- Gravity.CENTER);
- mIconFrame.addView(mIcon, params);
- mIconFrame.setClipChildren(false);
- mIconFrame.setClipToPadding(false);
-
- mTileBackground = newTileBackground();
- if (mTileBackground instanceof RippleDrawable) {
- setRipple((RippleDrawable) mTileBackground);
- }
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- setBackground(mTileBackground);
-
- mColorActive = Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.colorAccentPrimary);
- mColorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor);
-
- setPadding(0, 0, 0, 0);
- setClipChildren(false);
- setClipToPadding(false);
- mCollapsedView = collapsedView;
- setFocusable(true);
- }
-
- public View getBgCircle() {
- return mBg;
- }
-
- protected Drawable newTileBackground() {
- final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
- final TypedArray ta = getContext().obtainStyledAttributes(attrs);
- final Drawable d = ta.getDrawable(0);
- ta.recycle();
- return d;
- }
-
- private void setRipple(RippleDrawable tileBackground) {
- mRipple = tileBackground;
- if (getWidth() != 0) {
- updateRippleSize();
- }
- }
-
- protected void updateRippleSize() {
- // center the touch feedback on the center of the icon, and dial it down a bit
- final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
- final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
- final int rad = (int) (mIcon.getHeight() * .85f);
- mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
- }
-
- @Override
- public void init(QSTile tile) {
- init(v -> tile.click(this), v -> tile.secondaryClick(this), view -> {
- tile.longClick(this);
- return true;
- });
- }
-
- public void init(OnClickListener click, OnClickListener secondaryClick,
- OnLongClickListener longClick) {
- setOnClickListener(click);
- setOnLongClickListener(longClick);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- if (mRipple != null) {
- updateRippleSize();
- }
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- // Avoid layers for this layout - we don't need them.
- return false;
- }
-
- /**
- * Update the accessibility order for this view.
- *
- * @param previousView the view which should be before this one
- * @return the last view in this view which is accessible
- */
- public View updateAccessibilityOrder(View previousView) {
- setAccessibilityTraversalAfter(previousView.getId());
- return this;
- }
-
- public void onStateChanged(QSTile.State state) {
- mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
- }
-
- private void updateStrokeShapeWidth(QSTile.State state) {
- Resources resources = getContext().getResources();
- if (!(mBg.getDrawable() instanceof ShapeDrawable)) {
- return;
- }
- ShapeDrawable d = (ShapeDrawable) mBg.getDrawable();
- d.getPaint().setStyle(Paint.Style.FILL);
- switch (state.state) {
- case Tile.STATE_INACTIVE:
- if (mStrokeWidthInactive >= 0) {
- d.getPaint().setStyle(Paint.Style.STROKE);
- d.getPaint().setStrokeWidth(mStrokeWidthInactive);
- }
- break;
- case Tile.STATE_ACTIVE:
- if (mStrokeWidthActive >= 0) {
- d.getPaint().setStyle(Paint.Style.STROKE);
- d.getPaint().setStrokeWidth(mStrokeWidthActive);
- }
- break;
- }
- }
-
- protected void handleStateChanged(QSTile.State state) {
- updateStrokeShapeWidth(state);
- int circleColor = getCircleColor(state.state);
- boolean allowAnimations = animationsEnabled();
- if (circleColor != mCircleColor) {
- if (allowAnimations) {
- ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
- .setDuration(QS_ANIM_LENGTH);
- animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
- (Integer) animation.getAnimatedValue())));
- animator.start();
- } else {
- QSIconViewImpl.setTint(mBg, circleColor);
- }
- mCircleColor = circleColor;
- }
-
- mShowRippleEffect = state.showRippleEffect;
- setClickable(state.state != Tile.STATE_UNAVAILABLE);
- setLongClickable(state.handlesLongClick);
- mIcon.setIcon(state, allowAnimations);
- setContentDescription(state.contentDescription);
- final StringBuilder stateDescription = new StringBuilder();
- String text = "";
- switch (state.state) {
- case Tile.STATE_UNAVAILABLE:
- text = mContext.getString(R.string.tile_unavailable);
- break;
- case Tile.STATE_INACTIVE:
- if (state instanceof QSTile.BooleanState) {
- text = mContext.getString(R.string.switch_bar_off);
- }
- break;
- case Tile.STATE_ACTIVE:
- if (state instanceof QSTile.BooleanState) {
- text = mContext.getString(R.string.switch_bar_on);
- }
- break;
- default:
- break;
- }
- if (!TextUtils.isEmpty(text)) {
- stateDescription.append(text);
- if (TextUtils.isEmpty(state.secondaryLabel)) {
- state.secondaryLabel = text;
- }
- }
- if (!TextUtils.isEmpty(state.stateDescription)) {
- stateDescription.append(", ");
- stateDescription.append(state.stateDescription);
- if (mLastState != INVALID && state.state == mLastState
- && !state.stateDescription.equals(mLastStateDescription)) {
- mStateDescriptionDeltas = state.stateDescription;
- }
- }
- setStateDescription(stateDescription.toString());
- mLastState = state.state;
- mLastStateDescription = state.stateDescription;
-
- mAccessibilityClass =
- state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
- if (state instanceof QSTile.BooleanState) {
- boolean newState = ((BooleanState) state).value;
- if (mTileState != newState) {
- mTileState = newState;
- }
- }
- }
-
- /* The view should not be animated if it's not on screen and no part of it is visible.
- */
- protected boolean animationsEnabled() {
- if (!isShown()) {
- return false;
- }
- if (getAlpha() != 1f) {
- return false;
- }
- getLocationOnScreen(mLocInScreen);
- return mLocInScreen[1] >= -getHeight();
- }
-
- protected int getCircleColor(int state) {
- switch (state) {
- case Tile.STATE_ACTIVE:
- return mColorActive;
- case Tile.STATE_INACTIVE:
- case Tile.STATE_UNAVAILABLE:
- return mColorInactive;
- default:
- Log.e(TAG, "Invalid state " + state);
- return 0;
- }
- }
-
- @Override
- public void setClickable(boolean clickable) {
- super.setClickable(clickable);
- setBackground(clickable && mShowRippleEffect ? mRipple : null);
- }
-
- @Override
- public int getDetailY() {
- return getTop() + getHeight() / 2;
- }
-
- public QSIconView getIcon() {
- return mIcon;
- }
-
- public View getIconWithBackground() {
- return mIconFrame;
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- if (!TextUtils.isEmpty(mAccessibilityClass)) {
- event.setClassName(mAccessibilityClass);
- }
- if (event.getContentChangeTypes() == CONTENT_CHANGE_TYPE_STATE_DESCRIPTION
- && mStateDescriptionDeltas != null) {
- event.getText().add(mStateDescriptionDeltas);
- mStateDescriptionDeltas = null;
- }
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- // Clear selected state so it is not announce by talkback.
- info.setSelected(false);
- if (!TextUtils.isEmpty(mAccessibilityClass)) {
- info.setClassName(mAccessibilityClass);
- if (Switch.class.getName().equals(mAccessibilityClass)) {
- String label = getResources().getString(
- mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
- // Set the text here for tests in
- // android.platform.test.scenario.sysui.quicksettings. Can be removed when
- // UiObject2 has a new getStateDescription() API and tests are updated.
- info.setText(label);
- info.setChecked(mTileState);
- info.setCheckable(true);
- if (isLongClickable()) {
- info.addAction(
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfo.AccessibilityAction
- .ACTION_LONG_CLICK.getId(),
- getResources().getString(
- R.string.accessibility_long_click_tile)));
- }
- }
- }
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
- sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")");
- sb.append(", iconView=" + mIcon.toString());
- sb.append(", tileState=" + mTileState);
- sb.append("]");
- return sb.toString();
- }
-
- private class H extends Handler {
- private static final int STATE_CHANGED = 1;
-
- public H() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == STATE_CHANGED) {
- handleStateChanged((QSTile.State) msg.obj);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 5ff785d..47d80bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -38,7 +38,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.service.quicksettings.Tile;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -56,7 +55,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
@@ -563,21 +561,6 @@
return mReadyState == READY_STATE_READY;
}
- public static int getColorForState(Context context, int state) {
- switch (state) {
- case Tile.STATE_UNAVAILABLE:
- return Utils.getDisabled(context,
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary));
- case Tile.STATE_INACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
- case Tile.STATE_ACTIVE:
- return Utils.getColorAttrDefaultColor(context, android.R.attr.colorPrimary);
- default:
- Log.e("QSTile", "Invalid state " + state);
- return 0;
- }
- }
-
protected final class H extends Handler {
private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
deleted file mode 100644
index 50417e9..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs.tileimpl;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.service.quicksettings.Tile;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSIconView;
-import com.android.systemui.plugins.qs.QSTile;
-
-import java.util.Objects;
-
-/** View that represents a standard quick settings tile. **/
-public class QSTileView extends QSTileBaseView {
- protected int mMaxLabelLines = 2;
- private View mDivider;
- protected TextView mLabel;
- protected TextView mSecondLine;
- private ImageView mPadLock;
- protected int mState;
- protected ButtonRelativeLayout mLabelContainer;
- private View mExpandIndicator;
- private View mExpandSpace;
- protected ColorStateList mColorLabelActive;
- protected ColorStateList mColorLabelInactive;
- private ColorStateList mColorLabelUnavailable;
- protected boolean mDualTargetAllowed = false;
-
- public QSTileView(Context context, QSIconView icon) {
- this(context, icon, false);
- }
-
- public QSTileView(Context context, QSIconView icon, boolean collapsedView) {
- super(context, icon, collapsedView);
-
- setClipChildren(false);
- setClipToPadding(false);
-
- setClickable(true);
- setId(View.generateViewId());
- createLabel();
- setOrientation(VERTICAL);
- setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
- mColorLabelActive = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
- mColorLabelInactive = mColorLabelActive;
- // The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for
- // contrast purposes
- mColorLabelUnavailable = Utils.getColorAttr(getContext(),
- android.R.attr.textColorSecondary);
- }
-
- TextView getLabel() {
- return mLabel;
- }
-
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
- FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size);
- }
-
- @Override
- public int getDetailY() {
- return getTop() + mLabelContainer.getTop() + mLabelContainer.getHeight() / 2;
- }
-
- protected void createLabel() {
- mLabelContainer = (ButtonRelativeLayout) LayoutInflater.from(getContext())
- .inflate(R.layout.qs_tile_label, this, false);
- mLabelContainer.setClipChildren(false);
- mLabelContainer.setClipToPadding(false);
- mLabel = mLabelContainer.findViewById(R.id.tile_label);
- mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock);
- mDivider = mLabelContainer.findViewById(R.id.underline);
- mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
- mExpandSpace = mLabelContainer.findViewById(R.id.expand_space);
- mSecondLine = mLabelContainer.findViewById(R.id.app_label);
- addView(mLabelContainer);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mLabel.setSingleLine(false);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // Remeasure view if the primary label requires more than mMaxLabelLines lines or the
- // secondary label text will be cut off.
- if (shouldLabelBeSingleLine()) {
- mLabel.setSingleLine();
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- protected boolean shouldLabelBeSingleLine() {
- if (mCollapsedView) return true;
- if (mLabel.getLineCount() > mMaxLabelLines) {
- return true;
- } else if (!TextUtils.isEmpty(mSecondLine.getText())
- && mLabel.getLineCount() > mMaxLabelLines - 1) {
- return true;
- }
- return false;
- }
-
- @Override
- protected void handleStateChanged(QSTile.State state) {
- super.handleStateChanged(state);
- if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
- ColorStateList labelColor = getLabelColor(state.state);
- changeLabelColor(labelColor);
- mState = state.state;
- mLabel.setText(state.label);
- }
- if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
- mSecondLine.setText(state.secondaryLabel);
- mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel)
- ? View.GONE : View.VISIBLE);
- }
- boolean dualTarget = mDualTargetAllowed && state.dualTarget;
- handleExpand(dualTarget);
- mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
- : null);
- if (dualTarget != mLabelContainer.isClickable()) {
- mLabelContainer.setClickable(dualTarget);
- mLabelContainer.setLongClickable(dualTarget);
- mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
- }
- mLabel.setEnabled(!state.disabledByPolicy);
- mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
- }
-
- protected final ColorStateList getLabelColor(int state) {
- if (state == Tile.STATE_ACTIVE) {
- return mColorLabelActive;
- } else if (state == Tile.STATE_INACTIVE) {
- return mColorLabelInactive;
- }
- return mColorLabelUnavailable;
- }
-
- protected void changeLabelColor(ColorStateList color) {
- mLabel.setTextColor(color);
- }
-
- protected void handleExpand(boolean dualTarget) {
- mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
- mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
- }
-
- @Override
- public void init(OnClickListener click, OnClickListener secondaryClick,
- OnLongClickListener longClick) {
- super.init(click, secondaryClick, longClick);
- mLabelContainer.setOnClickListener(secondaryClick);
- mLabelContainer.setOnLongClickListener(longClick);
- mLabelContainer.setClickable(false);
- mLabelContainer.setLongClickable(false);
- }
-
- public TextView getAppLabel() {
- return mSecondLine;
- }
-
- @Nullable
- @Override
- public View getLabelContainer() {
- return mLabelContainer;
- }
-
- @Override
- public View getSecondaryLabel() {
- return mSecondLine;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
deleted file mode 100644
index 70d51ee..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tileimpl
-
-import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.graphics.drawable.Drawable
-import android.graphics.drawable.RippleDrawable
-import android.service.quicksettings.Tile.STATE_ACTIVE
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.RelativeLayout
-import com.android.systemui.R
-import com.android.systemui.plugins.qs.QSIconView
-import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
-
-open class QSTileViewHorizontal(
- context: Context,
- icon: QSIconView,
- collapsed: Boolean
-) : QSTileView(context, icon, collapsed), HeightOverrideable {
-
- protected var colorBackgroundDrawable: Drawable? = null
- private var paintColor = Color.WHITE
- private var paintAnimator: ValueAnimator? = null
- private var labelAnimator: ValueAnimator? = null
- private var sideView: ImageView = ImageView(mContext)
- override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
-
- init {
- orientation = HORIZONTAL
- gravity = Gravity.CENTER_VERTICAL or Gravity.START
- mDualTargetAllowed = false
- val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
- setPadding(padding, paddingTop, padding, paddingBottom)
-
- mBg.setImageDrawable(null)
- mIconFrame.removeAllViews()
- removeView(mIconFrame)
- val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
- addView(mIcon, 0, LayoutParams(iconSize, iconSize))
-
- sideView.visibility = View.GONE
- val sideViewLayoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
- gravity = Gravity.CENTER_VERTICAL
- marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
- }
- addView(sideView, -1, sideViewLayoutParams)
- sideView.adjustViewBounds = true
- sideView.scaleType = ImageView.ScaleType.FIT_CENTER
-
- mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
- changeLabelColor(getLabelColor(mState)) // Matches the default state of the tile
- }
-
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
- super.onLayout(changed, l, t, r, b)
- if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
- bottom = top + heightOverride
- }
- }
-
- override fun createLabel() {
- super.createLabel()
- findViewById<LinearLayout>(R.id.label_group)?.apply {
- gravity = Gravity.START
- (layoutParams as? RelativeLayout.LayoutParams)?.apply {
- removeRule(RelativeLayout.ALIGN_PARENT_TOP)
- }
- }
- mLabelContainer.setPadding(0, 0, 0, 0)
- (mLabelContainer.layoutParams as MarginLayoutParams).apply {
- marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
- marginEnd = 0
- gravity = Gravity.CENTER_VERTICAL or Gravity.START
- }
- mLabel.gravity = Gravity.START
- mLabel.textDirection = TEXT_DIRECTION_LOCALE
- mSecondLine.gravity = Gravity.START
- mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
-
- if (mCollapsedView) {
- mSecondLine.alpha = 0f
- mLabelContainer.setIgnoredView(mSecondLine)
- }
- }
-
- override fun shouldLabelBeSingleLine(): Boolean {
- return true
- }
-
- override fun updateRippleSize() {
- }
-
- override fun newTileBackground(): Drawable? {
- val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
- colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
- return ripple
- }
-
- override fun setClickable(clickable: Boolean) {
- super.setClickable(clickable)
- background = if (clickable && mShowRippleEffect) {
- mRipple?.also {
- // In case that the colorBackgroundDrawable was used as the background, make sure
- // it has the correct callback instead of null
- colorBackgroundDrawable?.callback = it
- }
- } else {
- colorBackgroundDrawable
- }
- }
-
- override fun handleStateChanged(state: QSTile.State) {
- super.handleStateChanged(state)
- mLabelContainer.background = null
-
- val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
- val newColor = getCircleColor(state.state)
- if (allowAnimations) {
- animateBackground(newColor)
- } else {
- clearBackgroundAnimator()
- colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
- paintColor = newColor
- }
- paintColor = newColor
- }
- loadSideViewDrawableIfNecessary(state)
- }
-
- private fun animateBackground(newBackgroundColor: Int) {
- if (newBackgroundColor != paintColor) {
- clearBackgroundAnimator()
- paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener { animation: ValueAnimator ->
- val c = animation.animatedValue as Int
- colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also {
- paintColor = c
- }
- }
- start()
- }
- }
- }
-
- override fun changeLabelColor(color: ColorStateList) {
- val allowAnimations = animationsEnabled()
- val currentColor = mLabel.textColors.defaultColor
- if (currentColor != color.defaultColor) {
- clearLabelAnimator()
- if (allowAnimations) {
- labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor)
- .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
- addUpdateListener {
- setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int))
- }
- start()
- }
- } else {
- setLabelsColor(color)
- }
- }
- }
-
- private fun setLabelsColor(color: ColorStateList) {
- mLabel.setTextColor(color)
- mSecondLine.setTextColor(color)
- }
-
- private fun clearBackgroundAnimator() {
- paintAnimator?.cancel()?.also { paintAnimator = null }
- }
-
- private fun clearLabelAnimator() {
- labelAnimator?.cancel()?.also { labelAnimator = null }
- }
-
- private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
- if (state.sideViewDrawable != null) {
- sideView.setImageDrawable(state.sideViewDrawable)
- sideView.visibility = View.VISIBLE
- } else {
- sideView.setImageDrawable(null)
- sideView.visibility = GONE
- }
- }
-
- override fun handleExpand(dualTarget: Boolean) {}
-
- override fun getSecondaryIcon(): View {
- return sideView
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
new file mode 100644
index 0000000..3ad95d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.Configuration
+import android.content.res.Resources.ID_NULL
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.RippleDrawable
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.util.Log
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.Switch
+import android.widget.TextView
+import com.android.settingslib.Utils
+import com.android.systemui.FontSizeUtils
+import com.android.systemui.R
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.QSTile.BooleanState
+import com.android.systemui.plugins.qs.QSTileView
+import java.util.Objects
+
+private const val TAG = "QSTileViewImpl"
+open class QSTileViewImpl @JvmOverloads constructor(
+ context: Context,
+ private val _icon: QSIconView,
+ private val collapsed: Boolean = false
+) : QSTileView(context), HeightOverrideable {
+
+ companion object {
+ private const val INVALID = -1
+ }
+
+ override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
+
+ private val colorActive = Utils.getColorAttrDefaultColor(context,
+ com.android.internal.R.attr.colorAccentPrimary)
+ private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor)
+ private val colorUnavailable =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.colorBackground)
+
+ private val colorLabelActive =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimaryInverse)
+ private val colorLabelInactive =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ private val colorLabelUnavailable =
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary)
+
+ private lateinit var label: TextView
+ protected lateinit var secondaryLabel: TextView
+ private lateinit var labelContainer: IgnorableChildLinearLayout
+ protected lateinit var sideView: ViewGroup
+ private lateinit var customDrawableView: ImageView
+ private lateinit var chevronView: ImageView
+
+ protected var showRippleEffect = true
+
+ private lateinit var ripple: RippleDrawable
+ private lateinit var colorBackgroundDrawable: Drawable
+ private var paintColor = Color.WHITE
+ private var paintAnimator: ValueAnimator? = null
+ private var labelAnimator: ValueAnimator? = null
+ private var secondaryLabelAnimator: ValueAnimator? = null
+
+ private var accessibilityClass: String? = null
+ private var stateDescriptionDeltas: CharSequence? = null
+ private var lastStateDescription: CharSequence? = null
+ private var tileState = false
+ private var lastState = INVALID
+
+ private val locInScreen = IntArray(2)
+
+ init {
+ setId(generateViewId())
+ orientation = LinearLayout.HORIZONTAL
+ gravity = Gravity.CENTER_VERTICAL or Gravity.START
+ importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES
+ clipChildren = false
+ clipToPadding = false
+ isFocusable = true
+ background = createTileBackground()
+
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
+ val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
+ setPaddingRelative(startPadding, padding, padding, padding)
+
+ val iconSize = resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ addView(_icon, LayoutParams(iconSize, iconSize))
+
+ createAndAddLabels()
+ createAndAddSideView()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration?) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
+ fun updateResources() {
+ FontSizeUtils.updateFontSize(label, R.dimen.qs_tile_text_size)
+ FontSizeUtils.updateFontSize(secondaryLabel, R.dimen.qs_tile_text_size)
+
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+ _icon.layoutParams.apply {
+ height = iconSize
+ width = iconSize
+ }
+
+ val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
+ val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
+ setPaddingRelative(startPadding, padding, padding, padding)
+
+ val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+ (labelContainer.layoutParams as MarginLayoutParams).apply {
+ marginStart = labelMargin
+ }
+
+ (sideView.layoutParams as MarginLayoutParams).apply {
+ marginStart = labelMargin
+ }
+ (chevronView.layoutParams as MarginLayoutParams).apply {
+ height = iconSize
+ width = iconSize
+ }
+
+ val endMargin = resources.getDimensionPixelSize(R.dimen.qs_drawable_end_margin)
+ (customDrawableView.layoutParams as MarginLayoutParams).apply {
+ height = iconSize
+ marginEnd = endMargin
+ }
+ }
+
+ private fun createAndAddLabels() {
+ labelContainer = LayoutInflater.from(context)
+ .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout
+ label = labelContainer.requireViewById(R.id.tile_label)
+ secondaryLabel = labelContainer.requireViewById(R.id.app_label)
+ if (collapsed) {
+ labelContainer.ignoreLastView = true
+ secondaryLabel.alpha = 0f
+ }
+ label.setTextColor(getLabelColor(0)) // Default state
+ secondaryLabel.setTextColor(getSecondaryLabelColor(0))
+ addView(labelContainer)
+ }
+
+ private fun createAndAddSideView() {
+ sideView = LayoutInflater.from(context)
+ .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
+ customDrawableView = sideView.requireViewById(R.id.customDrawable)
+ chevronView = sideView.requireViewById(R.id.chevron)
+ addView(sideView)
+ }
+
+ fun createTileBackground(): Drawable {
+ ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+ colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+ return ripple
+ }
+
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ super.onLayout(changed, l, t, r, b)
+ if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+ bottom = top + heightOverride
+ }
+ }
+
+ override fun updateAccessibilityOrder(previousView: View?): View {
+ accessibilityTraversalAfter = previousView?.id ?: ID_NULL
+ return this
+ }
+
+ override fun getIcon(): QSIconView {
+ return _icon
+ }
+
+ override fun getIconWithBackground(): View {
+ return icon
+ }
+
+ override fun init(tile: QSTile) {
+ init(
+ { v: View? -> tile.click(this) },
+ { view: View? ->
+ tile.longClick(this)
+ true
+ }
+ )
+ }
+
+ private fun init(
+ click: OnClickListener?,
+ longClick: OnLongClickListener?
+ ) {
+ setOnClickListener(click)
+ onLongClickListener = longClick
+ }
+
+ override fun onStateChanged(state: QSTile.State) {
+ post {
+ handleStateChanged(state)
+ }
+ }
+
+ override fun getDetailY(): Int {
+ return top + height / 2
+ }
+
+ override fun hasOverlappingRendering(): Boolean {
+ // Avoid layers for this layout - we don't need them.
+ return false
+ }
+
+ override fun setClickable(clickable: Boolean) {
+ super.setClickable(clickable)
+ background = if (clickable && showRippleEffect) {
+ ripple.also {
+ // In case that the colorBackgroundDrawable was used as the background, make sure
+ // it has the correct callback instead of null
+ colorBackgroundDrawable.callback = it
+ }
+ } else {
+ colorBackgroundDrawable
+ }
+ }
+
+ override fun getLabelContainer(): View {
+ return labelContainer
+ }
+
+ override fun getSecondaryLabel(): View {
+ return secondaryLabel
+ }
+
+ override fun getSecondaryIcon(): View {
+ return sideView
+ }
+
+ // Accessibility
+
+ override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {
+ super.onInitializeAccessibilityEvent(event)
+ if (!TextUtils.isEmpty(accessibilityClass)) {
+ event.className = accessibilityClass
+ }
+ if (event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION &&
+ stateDescriptionDeltas != null) {
+ event.text.add(stateDescriptionDeltas)
+ stateDescriptionDeltas = null
+ }
+ }
+
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo) {
+ super.onInitializeAccessibilityNodeInfo(info)
+ // Clear selected state so it is not announce by talkback.
+ info.isSelected = false
+ if (!TextUtils.isEmpty(accessibilityClass)) {
+ info.className = accessibilityClass
+ if (Switch::class.java.name == accessibilityClass) {
+ val label = resources.getString(
+ if (tileState) R.string.switch_bar_on else R.string.switch_bar_off)
+ // Set the text here for tests in
+ // android.platform.test.scenario.sysui.quicksettings. Can be removed when
+ // UiObject2 has a new getStateDescription() API and tests are updated.
+ info.text = label
+ info.isChecked = tileState
+ info.isCheckable = true
+ if (isLongClickable) {
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+ resources.getString(
+ R.string.accessibility_long_click_tile)))
+ }
+ }
+ }
+ }
+
+ override fun toString(): String {
+ val sb = StringBuilder(javaClass.simpleName).append('[')
+ sb.append("locInScreen=(${locInScreen[0]}, ${locInScreen[1]})")
+ sb.append(", iconView=$_icon")
+ sb.append(", tileState=$tileState")
+ sb.append("]")
+ return sb.toString()
+ }
+
+ // HANDLE STATE CHANGES RELATED METHODS
+
+ protected open fun handleStateChanged(state: QSTile.State) {
+ val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
+ showRippleEffect = state.showRippleEffect
+ isClickable = state.state != Tile.STATE_UNAVAILABLE
+ isLongClickable = state.handlesLongClick
+ icon.setIcon(state, allowAnimations)
+ contentDescription = state.contentDescription
+
+ // Background color animation
+ val newColor = getCircleColor(state.state)
+ if (allowAnimations) {
+ animateBackground(newColor)
+ } else {
+ clearBackgroundAnimator()
+ colorBackgroundDrawable.setTintList(ColorStateList.valueOf(newColor)).also {
+ paintColor = newColor
+ }
+ paintColor = newColor
+ }
+ //
+
+ // State handling and description
+ val stateDescription = StringBuilder()
+ val stateText = getStateText(state)
+ if (!TextUtils.isEmpty(stateText)) {
+ stateDescription.append(stateText)
+ if (TextUtils.isEmpty(state.secondaryLabel)) {
+ state.secondaryLabel = stateText
+ }
+ }
+ if (!TextUtils.isEmpty(state.stateDescription)) {
+ stateDescription.append(", ")
+ stateDescription.append(state.stateDescription)
+ if (lastState != INVALID && state.state == lastState &&
+ state.stateDescription != lastStateDescription) {
+ stateDescriptionDeltas = state.stateDescription
+ }
+ }
+
+ setStateDescription(stateDescription.toString())
+ lastStateDescription = state.stateDescription
+
+ accessibilityClass = if (state.state == Tile.STATE_UNAVAILABLE) {
+ null
+ } else {
+ state.expandedAccessibilityClassName
+ }
+
+ if (state is BooleanState) {
+ val newState = state.value
+ if (tileState != newState) {
+ tileState = newState
+ }
+ }
+ //
+
+ // Labels
+ if (!Objects.equals(label.text, state.label)) {
+ label.text = state.label
+ }
+ if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) {
+ secondaryLabel.text = state.secondaryLabel
+ secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) {
+ GONE
+ } else {
+ VISIBLE
+ }
+ }
+
+ if (allowAnimations) {
+ animateLabelColor(getLabelColor(state.state))
+ animateSecondaryLabelColor(getSecondaryLabelColor(state.state))
+ } else {
+ label.setTextColor(getLabelColor(state.state))
+ secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
+ }
+
+ // Right side icon
+ loadSideViewDrawableIfNecessary(state)
+ chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
+
+ label.isEnabled = !state.disabledByPolicy
+
+ lastState = state.state
+ }
+
+ private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
+ if (state.sideViewCustomDrawable != null) {
+ customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
+ customDrawableView.visibility = VISIBLE
+ chevronView.visibility = GONE
+ } else if (state !is BooleanState || state.forceExpandIcon) {
+ customDrawableView.setImageDrawable(null)
+ customDrawableView.visibility = GONE
+ chevronView.visibility = VISIBLE
+ } else {
+ customDrawableView.setImageDrawable(null)
+ customDrawableView.visibility = GONE
+ chevronView.visibility = GONE
+ }
+ }
+
+ private fun getStateText(state: QSTile.State): String {
+ return if (state.disabledByPolicy) {
+ context.getString(R.string.tile_disabled)
+ } else if (state.state == Tile.STATE_UNAVAILABLE) {
+ context.getString(R.string.tile_unavailable)
+ } else if (state is BooleanState) {
+ if (state.state == Tile.STATE_INACTIVE) {
+ context.getString(R.string.switch_bar_off)
+ } else {
+ context.getString(R.string.switch_bar_on)
+ }
+ } else {
+ ""
+ }
+ }
+
+ /*
+ * The view should not be animated if it's not on screen and no part of it is visible.
+ */
+ protected open fun animationsEnabled(): Boolean {
+ if (!isShown) {
+ return false
+ }
+ if (alpha != 1f) {
+ return false
+ }
+ getLocationOnScreen(locInScreen)
+ return locInScreen.get(1) >= -height
+ }
+
+ private fun animateBackground(newBackgroundColor: Int) {
+ if (newBackgroundColor != paintColor) {
+ clearBackgroundAnimator()
+ paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener { animation: ValueAnimator ->
+ val c = animation.animatedValue as Int
+ colorBackgroundDrawable.setTintList(ColorStateList.valueOf(c)).also {
+ paintColor = c
+ }
+ }
+ start()
+ }
+ }
+ }
+
+ private fun animateLabelColor(color: Int) {
+ val currentColor = label.textColors.defaultColor
+ if (currentColor != color) {
+ clearLabelAnimator()
+ labelAnimator = ValueAnimator.ofArgb(currentColor, color)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener {
+ label.setTextColor(it.animatedValue as Int)
+ }
+ start()
+ }
+ }
+ }
+
+ private fun animateSecondaryLabelColor(color: Int) {
+ val currentColor = secondaryLabel.textColors.defaultColor
+ if (currentColor != color) {
+ clearSecondaryLabelAnimator()
+ secondaryLabelAnimator = ValueAnimator.ofArgb(currentColor, color)
+ .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+ addUpdateListener {
+ secondaryLabel.setTextColor(it.animatedValue as Int)
+ }
+ start()
+ }
+ }
+ }
+
+ private fun clearBackgroundAnimator() {
+ paintAnimator?.cancel()?.also { paintAnimator = null }
+ }
+
+ private fun clearLabelAnimator() {
+ labelAnimator?.cancel()?.also { labelAnimator = null }
+ }
+
+ private fun clearSecondaryLabelAnimator() {
+ secondaryLabelAnimator?.cancel()?.also { secondaryLabelAnimator = null }
+ }
+
+ private fun getCircleColor(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> colorActive
+ Tile.STATE_INACTIVE -> colorInactive
+ Tile.STATE_UNAVAILABLE -> colorUnavailable
+ else -> {
+ Log.e(TAG, "Invalid state $state")
+ 0
+ }
+ }
+ }
+
+ private fun getLabelColor(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> colorLabelActive
+ Tile.STATE_INACTIVE -> colorLabelInactive
+ Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
+ else -> {
+ Log.e(TAG, "Invalid state $state")
+ 0
+ }
+ }
+ }
+
+ private fun getSecondaryLabelColor(state: Int): Int {
+ return when (state) {
+ Tile.STATE_ACTIVE -> colorLabelActive
+ Tile.STATE_INACTIVE, Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
+ else -> {
+ Log.e(TAG, "Invalid state $state")
+ 0
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 5dcbf49..577c0d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -99,7 +99,9 @@
@Override
public SignalState newTileState() {
- return new SignalState();
+ SignalState s = new SignalState();
+ s.forceExpandIcon = true;
+ return s;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index d1b74cd..85b835a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -174,7 +174,7 @@
} else {
state.state = Tile.STATE_UNAVAILABLE;
}
- state.sideViewDrawable = isDeviceLocked ? null : mCardViewDrawable;
+ state.sideViewCustomDrawable = isDeviceLocked ? null : mCardViewDrawable;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index ef2c1c9..7bde64b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -129,6 +129,8 @@
state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.secondaryLabel = state.value ? "" :
+ mContext.getString(R.string.quick_settings_work_mode_paused);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index fad7480..eb7854e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.dagger;
+import android.app.IActivityManager;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Handler;
@@ -68,6 +69,7 @@
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
+import java.util.concurrent.Executor;
import dagger.Binds;
import dagger.Lazy;
@@ -239,10 +241,13 @@
CommonNotifCollection notifCollection,
FeatureFlags featureFlags,
SystemClock systemClock,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ @Main Executor mainExecutor,
+ IActivityManager iActivityManager) {
OngoingCallController ongoingCallController =
new OngoingCallController(
- notifCollection, featureFlags, systemClock, activityStarter);
+ notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
+ iActivityManager);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index b3f7ca6f..eb3a17f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -181,12 +181,19 @@
designatedCorner = newCorner
if (animationScheduler.hasPersistentDot) {
- designatedCorner!!.visibility = View.VISIBLE
- designatedCorner!!.alpha = 0f
- designatedCorner!!.animate()
- .alpha(1.0f)
- .setDuration(300)
- .start()
+ fadeInDot()
+ }
+ }
+
+ @UiThread
+ private fun fadeInDot() {
+ designatedCorner?.let { dot ->
+ dot.visibility = View.VISIBLE
+ dot.alpha = 0f
+ dot.animate()
+ .alpha(1.0f)
+ .setDuration(300)
+ .start()
}
}
@@ -300,9 +307,14 @@
private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
object : SystemStatusAnimationCallback {
- override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
+ override fun onSystemStatusAnimationTransitionToPersistentDot(
+ showAnimation: Boolean
+ ): Animator? {
if (designatedCorner == null) {
return null
+ } else if (!showAnimation) {
+ uiExecutor?.execute { fadeInDot() }
+ return null
}
val alpha = ObjectAnimator.ofFloat(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
index 398f5e3..539020d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
@@ -31,6 +31,8 @@
val priority: Int
// Whether or not to force the status bar open and show a dot
val forceVisible: Boolean
+ // Whether or not to show an animation for this event
+ val showAnimation: Boolean
val viewCreator: (context: Context) -> View
// Update this event with values from another event.
@@ -47,6 +49,7 @@
class BatteryEvent : StatusEvent {
override val priority = 50
override val forceVisible = false
+ override val showAnimation = true
override val viewCreator: (context: Context) -> View = { context ->
val iv = ImageView(context)
@@ -59,7 +62,7 @@
return javaClass.simpleName
}
}
-class PrivacyEvent : StatusEvent {
+class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
override val priority = 100
override val forceVisible = true
var privacyItems: List<PrivacyItem> = listOf()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index bde085e..ba50659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -16,10 +16,13 @@
package com.android.systemui.statusbar.events
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_PRIVACY
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
/**
@@ -28,6 +31,7 @@
*/
@SysUISingleton
class SystemEventCoordinator @Inject constructor(
+ private val systemClock: SystemClock,
private val batteryController: BatteryController,
private val privacyController: PrivacyItemController
) {
@@ -59,8 +63,8 @@
scheduler.setShouldShowPersistentPrivacyIndicator(false)
}
- fun notifyPrivacyItemsChanged() {
- val event = PrivacyEvent()
+ fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) {
+ val event = PrivacyEvent(showAnimation)
event.privacyItems = privacyStateListener.currentPrivacyItems
scheduler.onStatusEvent(event)
}
@@ -90,8 +94,17 @@
private val privacyStateListener = object : PrivacyItemController.Callback {
var currentPrivacyItems = listOf<PrivacyItem>()
+ var previousPrivacyItems = listOf<PrivacyItem>()
+ var timeLastEmpty = systemClock.elapsedRealtime()
override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ if (uniqueItemsMatch(privacyItems, currentPrivacyItems)) {
+ return
+ } else if (privacyItems.isEmpty()) {
+ previousPrivacyItems = currentPrivacyItems
+ timeLastEmpty = systemClock.elapsedRealtime()
+ }
+
currentPrivacyItems = privacyItems
notifyListeners()
}
@@ -100,10 +113,25 @@
if (currentPrivacyItems.isEmpty()) {
notifyPrivacyItemsEmpty()
} else {
- notifyPrivacyItemsChanged()
+ val showAnimation = isChipAnimationEnabled() &&
+ (!uniqueItemsMatch(currentPrivacyItems, previousPrivacyItems) ||
+ systemClock.elapsedRealtime() - timeLastEmpty >= DEBOUNCE_TIME)
+ notifyPrivacyItemsChanged(showAnimation)
}
}
+
+ // Return true if the lists contain the same permission groups, used by the same UIDs
+ private fun uniqueItemsMatch(one: List<PrivacyItem>, two: List<PrivacyItem>): Boolean {
+ return one.map { it.application.uid to it.privacyType.permGroupName }.toSet() ==
+ two.map { it.application.uid to it.privacyType.permGroupName }.toSet()
+ }
+
+ private fun isChipAnimationEnabled(): Boolean {
+ return DeviceConfig.getBoolean(NAMESPACE_PRIVACY, CHIP_ANIMATION_ENABLED, true)
+ }
}
}
+private const val DEBOUNCE_TIME = 3000L
+private const val CHIP_ANIMATION_ENABLED = "privacy_chip_animation_enabled"
private const val TAG = "SystemEventCoordinator"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 8da7fda..b6f0416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -103,7 +103,12 @@
if (DEBUG) {
Log.d(TAG, "scheduling event $event")
}
- scheduleEvent(event)
+ if (event.showAnimation) {
+ scheduleEvent(event)
+ } else if (event.forceVisible) {
+ hasPersistentDot = true
+ notifyTransitionToPersistentDot(showAnimation = false)
+ }
} else {
if (DEBUG) {
Log.d(TAG, "ignoring event $event")
@@ -197,7 +202,7 @@
aSet2.play(chipAnimator).before(systemAnimator)
if (hasPersistentDot) {
- val dotAnim = notifyTransitionToPersistentDot()
+ val dotAnim = notifyTransitionToPersistentDot(showAnimation = true)
if (dotAnim != null) aSet2.playTogether(systemAnimator, dotAnim)
}
@@ -209,9 +214,9 @@
}, DELAY)
}
- private fun notifyTransitionToPersistentDot(): Animator? {
+ private fun notifyTransitionToPersistentDot(showAnimation: Boolean): Animator? {
val anims: List<Animator> = listeners.mapNotNull {
- it.onSystemStatusAnimationTransitionToPersistentDot()
+ it.onSystemStatusAnimationTransitionToPersistentDot(showAnimation)
}
if (anims.isNotEmpty()) {
val aSet = AnimatorSet()
@@ -321,7 +326,9 @@
@JvmDefault fun onSystemChromeAnimationEnd() {}
// Best method name, change my mind
- @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
+ @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(
+ showAnimation: Boolean
+ ): Animator? { return null }
@JvmDefault fun onHidePersistentDot(): Animator? { return null }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 16bed6f..2b51b56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -105,12 +105,7 @@
private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() {
@Override
- public void onOngoingCallStarted(boolean animate) {
- disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
- }
-
- @Override
- public void onOngoingCallEnded(boolean animate) {
+ public void onOngoingCallStateChanged(boolean animate) {
disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index ae68462..c911e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -220,17 +220,22 @@
*/
private void onFullyHidden() {
cancelShowRunnable();
- if (mRoot != null) {
- mRoot.setVisibility(View.INVISIBLE);
- }
+ setVisibility(View.INVISIBLE);
mFalsingCollector.onBouncerHidden();
DejankUtils.postAfterTraversal(mResetRunnable);
}
+ private void setVisibility(@View.Visibility int visibility) {
+ if (mRoot != null) {
+ mRoot.setVisibility(visibility);
+ dispatchVisibilityChanged();
+ }
+ }
+
private final Runnable mShowRunnable = new Runnable() {
@Override
public void run() {
- mRoot.setVisibility(View.VISIBLE);
+ setVisibility(View.VISIBLE);
showPromptReason(mBouncerPromptReason);
final CharSequence customMessage = mCallback.consumeCustomMessage();
if (customMessage != null) {
@@ -299,7 +304,7 @@
}
mIsAnimatingAway = false;
if (mRoot != null) {
- mRoot.setVisibility(View.INVISIBLE);
+ setVisibility(View.INVISIBLE);
if (destroyView) {
// We have a ViewFlipper that unregisters a broadcast when being detached, which may
@@ -436,7 +441,7 @@
mContainer.addView(mRoot, mContainer.getChildCount());
mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
com.android.systemui.R.dimen.status_bar_height);
- mRoot.setVisibility(View.INVISIBLE);
+ setVisibility(View.INVISIBLE);
final WindowInsets rootInsets = mRoot.getRootWindowInsets();
if (rootInsets != null) {
@@ -533,6 +538,12 @@
}
}
+ private void dispatchVisibilityChanged() {
+ for (BouncerExpansionCallback callback : mExpansionCallbacks) {
+ callback.onVisibilityChanged(mRoot.getVisibility() == View.VISIBLE);
+ }
+ }
+
/**
* Apply keyguard configuration from the currently active resources. This can be called when the
* device configuration changes, to re-apply some resources that are qualified on the device
@@ -573,6 +584,13 @@
* to 1f {@link KeyguardBouncer#EXPANSION_HIDDEN} when fully hidden
*/
default void onExpansionChanged(float bouncerHideAmount) {}
+
+ /**
+ * Invoked when visibility of KeyguardBouncer has changed.
+ * Note the bouncer expansion can be {@link KeyguardBouncer#EXPANSION_VISIBLE}, but the
+ * view's visibility can be {@link View.INVISIBLE}.
+ */
+ default void onVisibilityChanged(boolean isVisible) {}
}
/** Create a {@link KeyguardBouncer} once a container and bouncer callback are available. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 5085e1c..d84bb90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -125,6 +125,7 @@
if (info != null) {
setTextColor(info.getTextColor());
setOnClickListener(info.getClickListener());
+ setClickable(info.getClickListener() != null);
final Drawable icon = info.getIcon();
if (icon != null) {
icon.setTint(getCurrentTextColor());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index bd17d00..cae7e35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -393,7 +393,6 @@
private final Object mQueueLock = new Object();
- private final StatusBarIconController mIconController;
private final PulseExpansionHandler mPulseExpansionHandler;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
private final KeyguardBypassController mKeyguardBypassController;
@@ -720,7 +719,7 @@
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
+ StatusBarSignalPolicy signalPolicy,
PulseExpansionHandler pulseExpansionHandler,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
@@ -806,7 +805,7 @@
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mIconController = statusBarIconController;
+ mSignalPolicy = signalPolicy;
mPulseExpansionHandler = pulseExpansionHandler;
mWakeUpCoordinator = notificationWakeUpCoordinator;
mKeyguardBypassController = keyguardBypassController;
@@ -1015,7 +1014,6 @@
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy.init();
- mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
mKeyguardStateController.addCallback(this);
startKeyguard();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1ef84701..6ed375e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -129,6 +129,13 @@
}
updateStates();
}
+
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ if (mAlternateAuthInterceptor != null) {
+ mAlternateAuthInterceptor.onBouncerVisibilityChanged();
+ }
+ }
};
private final DockManager.DockEventListener mDockEventListener =
new DockManager.DockEventListener() {
@@ -1126,6 +1133,18 @@
}
/**
+ * Request to show the udfps affordance in a particular color. This can be used if an
+ * occluding app on the keyguard would like to request udfps. This method does nothing if
+ * {@link KeyguardUpdateMonitor#shouldListenForFingerprint} is false.
+ */
+ public void requestUdfps(boolean request, int color) {
+ if (mAlternateAuthInterceptor == null) {
+ return;
+ }
+ mAlternateAuthInterceptor.requestUdfps(request, color);
+ }
+
+ /**
* Delegate used to send show/reset events to an alternate authentication method instead of the
* regular pin/pattern/password bouncer.
*/
@@ -1174,5 +1193,19 @@
*/
void setBouncerExpansionChanged(float expansion);
+ /**
+ * called when the bouncer view visibility has changed.
+ */
+ void onBouncerVisibilityChanged();
+
+ /**
+ * Use when an app occluding the keyguard would like to give the user ability to
+ * unlock the device using udfps.
+ *
+ * @param color of the udfps icon. should have proper contrast with its background. only
+ * used if requestUdfps = true
+ */
+ void requestUdfps(boolean requestUdfps, int color);
+
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 3445826..142cf21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -25,6 +25,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
@@ -33,12 +34,16 @@
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.CarrierConfigTracker;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import javax.inject.Inject;
+/** Controls the signal policies for icons shown in the StatusBar. **/
+@SysUISingleton
public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
SecurityController.SecurityControllerCallback, Tunable {
private static final String TAG = "StatusBarSignalPolicy";
@@ -57,6 +62,7 @@
private final NetworkController mNetworkController;
private final SecurityController mSecurityController;
private final Handler mHandler = Handler.getMain();
+ private final CarrierConfigTracker mCarrierConfigTracker;
private boolean mHideAirplane;
private boolean mHideMobile;
@@ -75,7 +81,9 @@
new ArrayList<CallIndicatorIconState>();
private WifiIconState mWifiIconState = new WifiIconState();
- public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
+ @Inject
+ public StatusBarSignalPolicy(Context context, StatusBarIconController iconController,
+ CarrierConfigTracker carrierConfigTracker) {
mContext = context;
mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
@@ -95,6 +103,7 @@
Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
+ mCarrierConfigTracker = carrierConfigTracker;
}
public void destroy() {
@@ -215,8 +224,12 @@
state.callStrengthResId = statusIcon.icon;
state.callStrengthDescription = statusIcon.contentDescription;
}
- mIconController.setCallStrengthIcons(mSlotCallStrength,
- CallIndicatorIconState.copyStates(mCallIndicatorStates));
+ if (mCarrierConfigTracker.getCallStrengthConfig(subId)) {
+ mIconController.setCallStrengthIcons(mSlotCallStrength,
+ CallIndicatorIconState.copyStates(mCallIndicatorStates));
+ } else {
+ mIconController.removeIcon(mSlotCallStrength, subId);
+ }
mIconController.setNoCallingIcons(mSlotNoCalling,
CallIndicatorIconState.copyStates(mCallIndicatorStates));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index ae11a74..d0d2cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -88,10 +88,10 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -134,7 +134,7 @@
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarIconController statusBarIconController,
+ StatusBarSignalPolicy signalPolicy,
PulseExpansionHandler pulseExpansionHandler,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
@@ -221,7 +221,7 @@
lightBarController,
autoHideController,
keyguardUpdateMonitor,
- statusBarIconController,
+ signalPolicy,
pulseExpansionHandler,
notificationWakeUpCoordinator,
keyguardBypassController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
new file mode 100644
index 0000000..1fe77fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import android.content.Context
+import android.util.AttributeSet
+
+import android.widget.Chronometer
+
+/**
+ * A [Chronometer] specifically for the ongoing call chip in the status bar.
+ *
+ * This class handles:
+ * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would
+ * change slightly each second because the width of each number is slightly different.
+ *
+ * Instead, we save the largest number width seen so far and ensure that the chip is at least
+ * that wide. This means the chip may get larger over time (e.g. in the transition from 59:59
+ * to 1:00:00), but never smaller.
+ *
+ * 2) Hiding the text if the time gets too long for the space available. Once the text has been
+ * hidden, it remains hidden for the duration of the call.
+ *
+ * Note that if the text was too big in portrait mode, resulting in the text being hidden, then the
+ * text will also be hidden in landscape (even if there is enough space for it in landscape).
+ */
+class OngoingCallChronometer @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyle: Int = 0
+) : Chronometer(context, attrs, defStyle) {
+
+ // Minimum width that the text view can be. Corresponds with the largest number width seen so
+ // far.
+ var minimumTextWidth: Int = 0
+
+ // True if the text is too long for the space available, so the text should be hidden.
+ var shouldHideText: Boolean = false
+
+ override fun setBase(base: Long) {
+ // These variables may have changed during the previous call, so re-set them before the new
+ // call starts.
+ minimumTextWidth = 0
+ shouldHideText = false
+ super.setBase(base)
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (shouldHideText) {
+ setMeasuredDimension(0, 0)
+ return
+ }
+
+ // Evaluate how wide the text *wants* to be if it had unlimited space.
+ super.onMeasure(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ heightMeasureSpec)
+ val desiredTextWidth = measuredWidth
+
+ // Evaluate how wide the text *can* be based on the enforced constraints
+ val enforcedTextWidth = resolveSize(desiredTextWidth, widthMeasureSpec)
+
+ if (desiredTextWidth > enforcedTextWidth) {
+ shouldHideText = true
+ setMeasuredDimension(0, 0)
+ } else {
+ // It's possible that the current text could fit in a smaller width, but we don't want
+ // the chip to change size every second. Instead, keep it at the minimum required width.
+ minimumTextWidth = desiredTextWidth.coerceAtLeast(minimumTextWidth)
+ setMeasuredDimension(minimumTextWidth, MeasureSpec.getSize(heightMeasureSpec))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 51bb643..6d1df5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone.ongoingcall
+import android.app.ActivityManager
+import android.app.IActivityManager
+import android.app.IUidObserver
import android.app.Notification
import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
import android.content.Intent
@@ -25,6 +28,7 @@
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,6 +36,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.Executor
import javax.inject.Inject
/**
@@ -42,12 +47,17 @@
private val notifCollection: CommonNotifCollection,
private val featureFlags: FeatureFlags,
private val systemClock: SystemClock,
- private val activityStarter: ActivityStarter
+ private val activityStarter: ActivityStarter,
+ @Main private val mainExecutor: Executor,
+ private val iActivityManager: IActivityManager
) : CallbackController<OngoingCallListener> {
/** Null if there's no ongoing call. */
private var ongoingCallInfo: OngoingCallInfo? = null
+ /** True if the application managing the call is visible to the user. */
+ private var isCallAppVisible: Boolean = true
private var chipView: ViewGroup? = null
+ private var uidObserver: IUidObserver.Stub? = null
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
@@ -67,8 +77,9 @@
override fun onEntryUpdated(entry: NotificationEntry) {
if (isOngoingCallNotification(entry)) {
ongoingCallInfo = OngoingCallInfo(
- entry.sbn.notification.`when`,
- entry.sbn.notification.contentIntent.intent)
+ entry.sbn.notification.`when`,
+ entry.sbn.notification.contentIntent.intent,
+ entry.sbn.uid)
updateChip()
}
}
@@ -76,7 +87,10 @@
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
if (isOngoingCallNotification(entry)) {
ongoingCallInfo = null
- mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) }
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ if (uidObserver != null) {
+ iActivityManager.unregisterUidObserver(uidObserver)
+ }
}
}
}
@@ -100,9 +114,13 @@
}
/**
- * Returns true if there's an active ongoing call that can be displayed in a status bar chip.
+ * Returns true if there's an active ongoing call that should be displayed in a status bar chip.
*/
- fun hasOngoingCall(): Boolean = ongoingCallInfo != null
+ fun hasOngoingCall(): Boolean {
+ return ongoingCallInfo != null &&
+ // When the user is in the phone app, don't show the chip.
+ !isCallAppVisible
+ }
override fun addCallback(listener: OngoingCallListener) {
synchronized(mListeners) {
@@ -137,7 +155,9 @@
ActivityLaunchAnimator.Controller.fromView(it))
}
- mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) }
+ setUpUidObserver(currentOngoingCallInfo)
+
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
} else {
// If we failed to update the chip, don't store the ongoing call info. Then
// [hasOngoingCall] will return false and we fall back to typical notification handling.
@@ -150,9 +170,52 @@
}
}
+ /**
+ * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call.
+ */
+ private fun setUpUidObserver(currentOngoingCallInfo: OngoingCallInfo) {
+ isCallAppVisible = isProcessVisibleToUser(
+ iActivityManager.getUidProcessState(currentOngoingCallInfo.uid, null))
+
+ uidObserver = object : IUidObserver.Stub() {
+ override fun onUidStateChanged(
+ uid: Int, procState: Int, procStateSeq: Long, capability: Int) {
+ if (uid == currentOngoingCallInfo.uid) {
+ val oldIsCallAppVisible = isCallAppVisible
+ isCallAppVisible = isProcessVisibleToUser(procState)
+ if (oldIsCallAppVisible != isCallAppVisible) {
+ // Animations may be run as a result of the call's state change, so ensure
+ // the listener is notified on the main thread.
+ mainExecutor.execute {
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ }
+ }
+ }
+ }
+
+ override fun onUidGone(uid: Int, disabled: Boolean) {}
+ override fun onUidActive(uid: Int) {}
+ override fun onUidIdle(uid: Int, disabled: Boolean) {}
+ override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
+ }
+
+ iActivityManager.registerUidObserver(
+ uidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_UNKNOWN,
+ null
+ )
+ }
+
+ /** Returns true if the given [procState] represents a process that's visible to the user. */
+ private fun isProcessVisibleToUser(procState: Int): Boolean {
+ return procState <= ActivityManager.PROCESS_STATE_TOP
+ }
+
private class OngoingCallInfo(
val callStartTime: Long,
- val intent: Intent
+ val intent: Intent,
+ val uid: Int
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
index 7c583a1..7a12430 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar.phone.ongoingcall
-/** A listener that's notified when an ongoing call is started or ended. */
+/** A listener that's notified when the state of an ongoing call has changed. */
interface OngoingCallListener {
- /** Called when an ongoing call is started. */
- fun onOngoingCallStarted(animate: Boolean)
- /** Called when an ongoing call is ended. */
- fun onOngoingCallEnded(animate: Boolean)
+ /**
+ * Called when the state of an ongoing call has changed in any way that may affect view
+ * visibility (including call starting, call stopping, application managing the call becoming
+ * visible or invisible).
+ */
+ fun onOngoingCallStateChanged(animate: Boolean)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 044f52f..ce08075 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -61,6 +61,7 @@
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -78,6 +79,7 @@
private static final int IMS_TYPE_WLAN = 2;
private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
private final TelephonyManager mPhone;
+ private final CarrierConfigTracker mCarrierConfigTracker;
private final ImsMmTelManager mImsMmTelManager;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
@@ -123,10 +125,12 @@
public MobileSignalController(Context context, Config config, boolean hasMobileData,
TelephonyManager phone, CallbackHandler callbackHandler,
NetworkControllerImpl networkController, SubscriptionInfo info,
- SubscriptionDefaults defaults, Looper receiverLooper) {
+ SubscriptionDefaults defaults, Looper receiverLooper,
+ CarrierConfigTracker carrierConfigTracker) {
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
networkController);
+ mCarrierConfigTracker = carrierConfigTracker;
mConfig = config;
mPhone = phone;
mDefaults = defaults;
@@ -583,7 +587,7 @@
mTelephonyDisplayInfo = mobileStatus.telephonyDisplayInfo;
int lastVoiceState = mServiceState != null ? mServiceState.getState() : -1;
mServiceState = mobileStatus.serviceState;
- int currentVoiceState = mServiceState != null ? mServiceState.getState() : -1;
+ int currentVoiceState = mServiceState != null ? mServiceState.getState() : -1;
// Only update the no calling Status in the below scenarios
// 1. The first valid voice state has been received
// 2. The voice state has been changed and either the last or current state is
@@ -594,12 +598,29 @@
|| (lastVoiceState == ServiceState.STATE_IN_SERVICE
|| currentVoiceState == ServiceState.STATE_IN_SERVICE))) {
boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
- IconState statusIcon = new IconState(isNoCalling, R.drawable.ic_qs_no_calling_sms,
+ isNoCalling &= !hideNoCalling();
+ IconState statusIcon = new IconState(isNoCalling,
+ R.drawable.ic_qs_no_calling_sms,
getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
}
}
+ void updateNoCallingState() {
+ int currentVoiceState = mServiceState != null ? mServiceState.getState() : -1;
+ boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
+ isNoCalling &= !hideNoCalling();
+ IconState statusIcon = new IconState(isNoCalling,
+ R.drawable.ic_qs_no_calling_sms,
+ getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
+ notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
+ }
+
+ private boolean hideNoCalling() {
+ return mNetworkController.hasDefaultNetwork()
+ && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
+ }
+
private int getCallStrengthIcon(int level, boolean isWifi) {
return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
: TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
@@ -616,7 +637,9 @@
void refreshCallIndicator(SignalCallback callback) {
boolean isNoCalling = mServiceState != null
&& mServiceState.getState() != ServiceState.STATE_IN_SERVICE;
- IconState statusIcon = new IconState(isNoCalling, R.drawable.ic_qs_no_calling_sms,
+ isNoCalling &= !hideNoCalling();
+ IconState statusIcon = new IconState(isNoCalling,
+ R.drawable.ic_qs_no_calling_sms,
getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
@@ -646,7 +669,6 @@
if (!mProviderModel) {
return;
}
- Log.d("mTag", "notifyWifiLevelChange " + mImsType);
mLastWlanLevel = level;
if (mImsType != IMS_TYPE_WLAN) {
return;
@@ -662,7 +684,6 @@
if (!mProviderModel) {
return;
}
- Log.d("mTag", "notifyDefaultMobileLevelChange " + mImsType);
mLastWlanCrossSimLevel = level;
if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
return;
@@ -681,7 +702,6 @@
int newLevel = getSignalLevel(signalStrength);
if (newLevel != mLastLevel) {
mLastLevel = newLevel;
- Log.d("mTag", "notifyMobileLevelChangeIfNecessary " + mImsType);
mLastWwanLevel = newLevel;
if (mImsType == IMS_TYPE_WWAN) {
IconState statusIcon = new IconState(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f683603..f45218d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -74,6 +74,7 @@
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.util.CarrierConfigTracker;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -122,6 +123,7 @@
private final Object mLock = new Object();
private final boolean mProviderModel;
private Config mConfig;
+ private final CarrierConfigTracker mCarrierConfigTracker;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -212,7 +214,8 @@
@Nullable WifiManager wifiManager,
NetworkScoreManager networkScoreManager,
AccessPointControllerImpl accessPointController,
- DemoModeController demoModeController) {
+ DemoModeController demoModeController,
+ CarrierConfigTracker carrierConfigTracker) {
this(context, connectivityManager,
telephonyManager,
telephonyListenerManager,
@@ -228,7 +231,8 @@
new SubscriptionDefaults(),
deviceProvisionedController,
broadcastDispatcher,
- demoModeController);
+ demoModeController,
+ carrierConfigTracker);
mReceiverHandler.post(mRegisterListeners);
}
@@ -246,7 +250,8 @@
SubscriptionDefaults defaultsHandler,
DeviceProvisionedController deviceProvisionedController,
BroadcastDispatcher broadcastDispatcher,
- DemoModeController demoModeController) {
+ DemoModeController demoModeController,
+ CarrierConfigTracker carrierConfigTracker) {
mContext = context;
mTelephonyListenerManager = telephonyListenerManager;
mConfig = config;
@@ -262,6 +267,7 @@
mConnectivityManager = connectivityManager;
mHasMobileDataFeature = telephonyManager.isDataCapable();
mDemoModeController = demoModeController;
+ mCarrierConfigTracker = carrierConfigTracker;
// telephony
mPhone = telephonyManager;
@@ -574,6 +580,10 @@
return mWifiSignalController.isCarrierMergedWifi(subId);
}
+ boolean hasDefaultNetwork() {
+ return !mNoDefaultNetwork;
+ }
+
boolean isNonCarrierWifiNetworkAvailable() {
return !mNoNetworksAvailable;
}
@@ -884,7 +894,7 @@
MobileSignalController controller = new MobileSignalController(mContext, mConfig,
mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
mCallbackHandler, this, subscriptions.get(i),
- mSubDefaults, mReceiverHandler.getLooper());
+ mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker);
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1027,6 +1037,10 @@
mNoDefaultNetwork = mConnectedTransports.isEmpty();
mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
mNoNetworksAvailable);
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+ mobileSignalController.updateNoCallingState();
+ }
notifyAllListeners();
}
}
@@ -1334,8 +1348,8 @@
null, null, null, "", false, null, null);
MobileSignalController controller = new MobileSignalController(mContext,
mConfig, mHasMobileDataFeature,
- mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this, info,
- mSubDefaults, mReceiverHandler.getLooper());
+ mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
+ info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker);
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
return info;
diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
new file mode 100644
index 0000000..02a07e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.util.SparseArray;
+
+import com.android.systemui.dagger.SysUISingleton;
+
+import javax.inject.Inject;
+
+/**
+ * Tracks the Carrier Config values.
+ */
+@SysUISingleton
+public class CarrierConfigTracker extends BroadcastReceiver {
+ private final SparseArray<Boolean> mCallStrengthConfigs = new SparseArray<>();
+ private final SparseArray<Boolean> mNoCallingConfigs = new SparseArray<>();
+ private final CarrierConfigManager mCarrierConfigManager;
+ private final boolean mDefaultCallStrengthConfig;
+ private final boolean mDefaultNoCallingConfig;
+
+ @Inject
+ public CarrierConfigTracker(Context context) {
+ mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
+ context.registerReceiver(
+ this, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ mDefaultCallStrengthConfig =
+ CarrierConfigManager.getDefaultConfig().getBoolean(
+ CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL);
+ mDefaultNoCallingConfig =
+ CarrierConfigManager.getDefaultConfig().getBoolean(
+ CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) {
+ int subId = intent.getIntExtra(
+ CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return;
+ }
+ PersistableBundle b = mCarrierConfigManager.getConfigForSubId(subId);
+ if (b != null) {
+ boolean hideNoCallingConfig = b.getBoolean(
+ CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL);
+ boolean displayCallStrengthIcon = b.getBoolean(
+ CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL);
+ mCallStrengthConfigs.put(subId, displayCallStrengthIcon);
+ mNoCallingConfigs.put(subId, hideNoCallingConfig);
+ }
+ }
+ }
+
+ /**
+ * Returns the KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL value for the given subId.
+ */
+ public boolean getCallStrengthConfig(int subId) {
+ if (mCallStrengthConfigs.indexOfKey(subId) >= 0) {
+ return mCallStrengthConfigs.get(subId);
+ }
+ return mDefaultCallStrengthConfig;
+ }
+
+ /**
+ * Returns the KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL value for the given subId.
+ */
+ public boolean getNoCallingConfig(int subId) {
+ if (mNoCallingConfigs.indexOfKey(subId) >= 0) {
+ return mNoCallingConfigs.get(subId);
+ }
+ return mDefaultNoCallingConfig;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 08cdebd..edea305 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -58,6 +58,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.io.FileDescriptor;
@@ -296,7 +297,7 @@
MemoryIconDrawable(Context context) {
baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
dp = context.getResources().getDisplayMetrics().density;
- paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE));
+ paint.setColor(QSIconViewImpl.getIconColorForState(context, STATE_ACTIVE));
}
public void setRss(long rss) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 92e68f3..ac8b16a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -16,6 +16,7 @@
package com.android.systemui.wallet.ui;
+import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -34,6 +35,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LifecycleActivity;
@@ -54,6 +56,7 @@
private final Handler mHandler;
private final FalsingManager mFalsingManager;
private final UserTracker mUserTracker;
+ private final StatusBarKeyguardViewManager mKeyguardViewManager;
private WalletScreenController mWalletScreenController;
@Inject
@@ -65,7 +68,8 @@
@Background Executor executor,
@Main Handler handler,
FalsingManager falsingManager,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ StatusBarKeyguardViewManager keyguardViewManager) {
mQuickAccessWalletClient = quickAccessWalletClient;
mKeyguardStateController = keyguardStateController;
mKeyguardDismissUtil = keyguardDismissUtil;
@@ -74,6 +78,7 @@
mHandler = handler;
mFalsingManager = falsingManager;
mUserTracker = userTracker;
+ mKeyguardViewManager = keyguardViewManager;
}
@Override
@@ -136,6 +141,13 @@
protected void onResume() {
super.onResume();
mWalletScreenController.queryWalletCards();
+ mKeyguardViewManager.requestUdfps(true, Color.BLACK);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mKeyguardViewManager.requestUdfps(false, -1);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
index 4200241..1e1b459 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
@@ -61,7 +61,7 @@
static final int CARD_ANIM_ALPHA_DELAY = 50;
private final Rect mSystemGestureExclusionZone = new Rect();
- private WalletCardCarouselAdapter mWalletCardCarouselAdapter;
+ private final WalletCardCarouselAdapter mWalletCardCarouselAdapter;
private int mExpectedViewWidth;
private int mCardMarginPx;
private int mCardWidthPx;
@@ -79,12 +79,6 @@
// also be used in DotIndicatorDecoration.
float mEdgeToCenterDistance = Float.MAX_VALUE;
private float mCardCenterToScreenCenterDistancePx = Float.MAX_VALUE;
- // When card data is loaded, this many cards should be animated as data is bound to them.
- private int mNumCardsToAnimate;
- // When card data is loaded, this is the position of the leftmost card to be animated.
- private int mCardAnimationStartPosition;
- // When card data is loaded, the animations may be delayed so that other animations can complete
- private int mExtraAnimationDelay;
interface OnSelectionListener {
/**
@@ -179,10 +173,6 @@
}
}
- void setExtraAnimationDelay(int extraAnimationDelay) {
- mExtraAnimationDelay = extraAnimationDelay;
- }
-
void setSelectionListener(OnSelectionListener selectionListener) {
mSelectionListener = selectionListener;
}
@@ -200,19 +190,14 @@
}
/**
- * Set card data. Returns true if carousel was empty, indicating that views will be animated
+ * Returns true if the data set is changed.
*/
- boolean setData(List<WalletCardViewInfo> data, int selectedIndex) {
- boolean wasEmpty = mWalletCardCarouselAdapter.getItemCount() == 0;
- mWalletCardCarouselAdapter.setData(data);
+ boolean setData(List<WalletCardViewInfo> data, int selectedIndex, boolean hasLockStateChanged) {
+ boolean hasDataChanged = mWalletCardCarouselAdapter.setData(data, hasLockStateChanged);
scrollToPosition(selectedIndex);
- if (wasEmpty) {
- mNumCardsToAnimate = numCardsOnScreen(data.size(), selectedIndex);
- mCardAnimationStartPosition = Math.max(selectedIndex - 1, 0);
- }
WalletCardViewInfo selectedCard = data.get(selectedIndex);
mCardScrollListener.onCardScroll(selectedCard, selectedCard, 0);
- return wasEmpty;
+ return hasDataChanged;
}
@Override
@@ -222,19 +207,6 @@
}
/**
- * The number of cards shown on screen when one of the cards is position in the center. This is
- * also the num
- */
- private static int numCardsOnScreen(int numCards, int selectedIndex) {
- if (numCards <= 2) {
- return numCards;
- }
- // When there are 3 or more cards, 3 cards will be shown unless the first or last card is
- // centered on screen.
- return selectedIndex > 0 && selectedIndex < (numCards - 1) ? 3 : 2;
- }
-
- /**
* The padding pushes the first and last cards in the list to the center when they are
* selected.
*/
@@ -439,9 +411,29 @@
return mData.get(position).getCardId().hashCode();
}
- void setData(List<WalletCardViewInfo> data) {
+ private boolean setData(List<WalletCardViewInfo> data, boolean hasLockedStateChanged) {
+ List<WalletCardViewInfo> oldData = mData;
mData = data;
- notifyDataSetChanged();
+ if (hasLockedStateChanged || !isUiEquivalent(oldData, data)) {
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isUiEquivalent(
+ List<WalletCardViewInfo> oldData, List<WalletCardViewInfo> newData) {
+ if (oldData.size() != newData.size()) {
+ return false;
+ }
+ for (int i = 0; i < newData.size(); i++) {
+ WalletCardViewInfo oldItem = oldData.get(i);
+ WalletCardViewInfo newItem = newData.get(i);
+ if (!oldItem.isUiEquivalent(newItem)) {
+ return false;
+ }
+ }
+ return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java
index 3d37320..08fbe4a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java
@@ -54,4 +54,11 @@
*/
@NonNull
PendingIntent getPendingIntent();
+
+ default boolean isUiEquivalent(WalletCardViewInfo other) {
+ if (other == null) {
+ return false;
+ }
+ return getCardId().equals(other.getCardId());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 44074f70..c547bb3 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -19,8 +19,6 @@
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DELAY;
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DURATION;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
@@ -29,6 +27,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -46,9 +45,8 @@
public class WalletView extends FrameLayout implements WalletCardCarousel.OnCardScrollListener {
private static final String TAG = "WalletView";
- private static final int CAROUSEL_IN_ANIMATION_DURATION = 300;
+ private static final int CAROUSEL_IN_ANIMATION_DURATION = 100;
private static final int CAROUSEL_OUT_ANIMATION_DURATION = 200;
- private static final int CARD_LABEL_ANIM_DELAY = 133;
private final WalletCardCarousel mCardCarousel;
private final ImageView mIcon;
@@ -57,14 +55,12 @@
private final Button mAppButton;
// Displays underneath the carousel, allow user to unlock device, verify card, etc.
private final Button mActionButton;
- private final Interpolator mInInterpolator;
private final Interpolator mOutInterpolator;
private final float mAnimationTranslationX;
private final ViewGroup mCardCarouselContainer;
private final TextView mErrorView;
private final ViewGroup mEmptyStateView;
private CharSequence mCenterCardText;
- private Drawable mCenterCardIcon;
private boolean mIsDeviceLocked = false;
public WalletView(Context context) {
@@ -83,8 +79,6 @@
mActionButton = requireViewById(R.id.wallet_action_button);
mErrorView = requireViewById(R.id.error_view);
mEmptyStateView = requireViewById(R.id.wallet_empty_state);
- mInInterpolator =
- AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mOutInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.accelerate_cubic);
mAnimationTranslationX = mCardCarousel.getCardWidthPx() / 4f;
@@ -109,7 +103,6 @@
Drawable centerCardIcon = centerCard.getIcon();
if (!TextUtils.equals(mCenterCardText, centerCardText)) {
mCenterCardText = centerCardText;
- mCenterCardIcon = centerCardIcon;
mCardLabel.setText(centerCardText);
mIcon.setImageDrawable(centerCardIcon);
}
@@ -134,39 +127,15 @@
*/
void showCardCarousel(
List<WalletCardViewInfo> data, int selectedIndex, boolean isDeviceLocked) {
+ boolean shouldAnimate =
+ mCardCarousel.setData(data, selectedIndex, mIsDeviceLocked != isDeviceLocked);
mIsDeviceLocked = isDeviceLocked;
- boolean shouldAnimate = mCardCarousel.setData(data, selectedIndex);
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
+ mEmptyStateView.setVisibility(GONE);
renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
- // If the empty state is visible, animate it away and delay the card carousel animation
- int emptyStateAnimDelay = 0;
- if (mEmptyStateView.getVisibility() == VISIBLE) {
- emptyStateAnimDelay = CARD_ANIM_ALPHA_DURATION;
- mEmptyStateView.animate()
- .alpha(0)
- .setDuration(emptyStateAnimDelay)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mEmptyStateView.setVisibility(GONE);
- }
- })
- .start();
- }
- mCardLabel.setAlpha(0f);
- mCardLabel.animate().alpha(1f)
- .setStartDelay(CARD_LABEL_ANIM_DELAY + emptyStateAnimDelay)
- .setDuration(CARD_ANIM_ALPHA_DURATION)
- .start();
- mCardCarousel.setExtraAnimationDelay(emptyStateAnimDelay);
- mCardCarousel.setTranslationX(mAnimationTranslationX);
- mCardCarousel.animate().translationX(0)
- .setInterpolator(mInInterpolator)
- .setDuration(CAROUSEL_IN_ANIMATION_DURATION)
- .setStartDelay(emptyStateAnimDelay)
- .start();
+ animateViewsShown(mIcon, mCardLabel, mActionButton);
}
}
@@ -277,6 +246,15 @@
}
}
+ private static void animateViewsShown(View... uiElements) {
+ for (View view : uiElements) {
+ if (view.getVisibility() == VISIBLE) {
+ view.setAlpha(0f);
+ view.animate().alpha(1f).setDuration(CAROUSEL_IN_ANIMATION_DURATION).start();
+ }
+ }
+ }
+
private static CharSequence getLabelText(WalletCardViewInfo card) {
String[] rawLabel = card.getLabel().toString().split("\\n");
return rawLabel.length == 2 ? rawLabel[0] : card.getLabel();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index d07a8da..495489f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.graphics.drawable.Icon;
import android.os.Handler;
import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
@@ -41,10 +42,11 @@
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
+import androidx.annotation.Nullable;
+
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -74,6 +76,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
@SmallTest
@@ -117,12 +120,12 @@
@Mock
ConfigurationController mConfigurationController;
@Mock
+ Optional<BcSmartspaceDataPlugin> mOptionalSmartspaceDataProvider;
+ @Mock
BcSmartspaceDataPlugin mSmartspaceDataProvider;
@Mock
SmartspaceView mSmartspaceView;
@Mock
- SystemUIFactory mSystemUIFactory;
- @Mock
ActivityStarter mActivityStarter;
@Mock
FalsingManager mFalsingManager;
@@ -162,7 +165,6 @@
when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
- when(mSystemUIFactory.getSmartspaceDataProvider()).thenReturn(mSmartspaceDataProvider);
mController = new KeyguardClockSwitchController(
mView,
mStatusBarStateController,
@@ -175,14 +177,14 @@
mExecutor,
mBatteryController,
mConfigurationController,
- mSystemUIFactory,
mActivityStarter,
mFalsingManager,
mKeyguardUpdateMonitor,
mBypassController,
mHandler,
mUserTracker,
- mSecureSettings
+ mSecureSettings,
+ mOptionalSmartspaceDataProvider
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
@@ -190,6 +192,8 @@
mStatusArea = new View(getContext());
when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea);
+ when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(true);
+ when(mOptionalSmartspaceDataProvider.get()).thenReturn(mSmartspaceDataProvider);
when(mSmartspaceDataProvider.getView(any())).thenReturn(mSmartspaceView);
}
@@ -260,7 +264,7 @@
@Test
public void testSmartspaceEnabledNoDataProviderShowsKeyguardStatusArea() {
when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
- when(mSystemUIFactory.getSmartspaceDataProvider()).thenReturn(null);
+ when(mOptionalSmartspaceDataProvider.isPresent()).thenReturn(false);
mController.init();
assertEquals(View.VISIBLE, mStatusArea.getVisibility());
@@ -389,5 +393,7 @@
public void setIntentStarter(IntentStarter intentStarter) { }
public void setFalsingManager(FalsingManager falsingManager) { }
+
+ public void setDnd(@Nullable Icon dndIcon, @Nullable String description) { }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 54cee84..24e47c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -35,7 +35,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
-import com.android.systemui.qs.tileimpl.QSTileView;
+import com.android.systemui.qs.tileimpl.QSTileViewImpl;
import org.junit.Before;
import org.junit.Test;
@@ -60,7 +60,7 @@
private QSPanelControllerBase.TileRecord createTileRecord() {
QSPanelControllerBase.TileRecord tileRecord = new QSPanelControllerBase.TileRecord();
tileRecord.tile = mock(QSTile.class);
- tileRecord.tileView = spy(new QSTileView(mContext, new QSIconViewImpl(mContext)));
+ tileRecord.tileView = spy(new QSTileViewImpl(mContext, new QSIconViewImpl(mContext)));
return tileRecord;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 59a5f03..876acc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
@@ -66,6 +67,8 @@
private CarrierTextManager.Builder mCarrierTextControllerBuilder;
@Mock
private CarrierTextManager mCarrierTextManager;
+ @Mock
+ private CarrierConfigTracker mCarrierConfigTracker;
private TestableLooper mTestableLooper;
@Before
@@ -99,7 +102,7 @@
mQSCarrierGroupController = new QSCarrierGroupController.Builder(
mActivityStarter, handler, TestableLooper.get(this).getLooper(),
- mNetworkController, mCarrierTextControllerBuilder, mContext)
+ mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker)
.setQSCarrierGroup(mQSCarrierGroup)
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
deleted file mode 100644
index 998e070..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileBaseViewTest.kt
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tileimpl
-
-import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
-import android.text.TextUtils
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.qs.QSIconView
-import com.android.systemui.plugins.qs.QSTile
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class QSTileBaseViewTest : SysuiTestCase() {
-
- @Mock
- private lateinit var iconView: QSIconView
-
- private lateinit var tileView: QSTileBaseView
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- tileView = QSTileBaseView(context, iconView, false)
- }
-
- @Test
- fun testSecondaryLabelNotModified_unavailable() {
- val state = QSTile.State()
- val testString = "TEST STRING"
- state.state = Tile.STATE_UNAVAILABLE
- state.secondaryLabel = testString
-
- tileView.handleStateChanged(state)
-
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
- }
-
- @Test
- fun testSecondaryLabelNotModified_booleanInactive() {
- val state = QSTile.BooleanState()
- val testString = "TEST STRING"
- state.state = Tile.STATE_INACTIVE
- state.secondaryLabel = testString
-
- tileView.handleStateChanged(state)
-
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
- }
-
- @Test
- fun testSecondaryLabelNotModified_booleanActive() {
- val state = QSTile.BooleanState()
- val testString = "TEST STRING"
- state.state = Tile.STATE_ACTIVE
- state.secondaryLabel = testString
-
- tileView.handleStateChanged(state)
-
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
- }
-
- @Test
- fun testSecondaryLabelNotModified_availableNotBoolean_inactive() {
- val state = QSTile.State()
- state.state = Tile.STATE_INACTIVE
- state.secondaryLabel = ""
-
- tileView.handleStateChanged(state)
-
- assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
- }
-
- @Test
- fun testSecondaryLabelNotModified_availableNotBoolean_active() {
- val state = QSTile.State()
- state.state = Tile.STATE_ACTIVE
- state.secondaryLabel = ""
-
- tileView.handleStateChanged(state)
-
- assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
- }
-
- @Test
- fun testSecondaryLabelDescription_unavailable() {
- val state = QSTile.State()
- state.state = Tile.STATE_UNAVAILABLE
- state.secondaryLabel = ""
-
- tileView.handleStateChanged(state)
-
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(
- context.getString(R.string.tile_unavailable)
- )
- }
-
- @Test
- fun testSecondaryLabelDescription_booleanInactive() {
- val state = QSTile.BooleanState()
- state.state = Tile.STATE_INACTIVE
- state.secondaryLabel = ""
-
- tileView.handleStateChanged(state)
-
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(
- context.getString(R.string.switch_bar_off)
- )
- }
-
- @Test
- fun testSecondaryLabelDescription_booleanActive() {
- val state = QSTile.BooleanState()
- state.state = Tile.STATE_ACTIVE
- state.secondaryLabel = ""
-
- tileView.handleStateChanged(state)
-
- assertThat(state.secondaryLabel as CharSequence).isEqualTo(
- context.getString(R.string.switch_bar_on)
- )
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
new file mode 100644
index 0000000..e5e2e53
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import android.text.TextUtils
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSTileViewImplTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var iconView: QSIconView
+ @Mock
+ private lateinit var customDrawable: Drawable
+
+ private lateinit var tileView: FakeTileView
+ private lateinit var customDrawableView: View
+ private lateinit var chevronView: View
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ tileView = FakeTileView(context, iconView, false)
+ customDrawableView = tileView.requireViewById(R.id.customDrawable)
+ chevronView = tileView.requireViewById(R.id.chevron)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_unavailable() {
+ val state = QSTile.State()
+ val testString = "TEST STRING"
+ state.state = Tile.STATE_UNAVAILABLE
+ state.secondaryLabel = testString
+
+ tileView.changeState(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_booleanInactive() {
+ val state = QSTile.BooleanState()
+ val testString = "TEST STRING"
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = testString
+
+ tileView.changeState(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_booleanActive() {
+ val state = QSTile.BooleanState()
+ val testString = "TEST STRING"
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = testString
+
+ tileView.changeState(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_availableNotBoolean_inactive() {
+ val state = QSTile.State()
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = ""
+
+ tileView.changeState(state)
+
+ assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
+ }
+
+ @Test
+ fun testSecondaryLabelNotModified_availableNotBoolean_active() {
+ val state = QSTile.State()
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = ""
+
+ tileView.changeState(state)
+
+ assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
+ }
+
+ @Test
+ fun testSecondaryLabelDescription_unavailable() {
+ val state = QSTile.State()
+ state.state = Tile.STATE_UNAVAILABLE
+ state.secondaryLabel = ""
+
+ tileView.changeState(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+ context.getString(R.string.tile_unavailable)
+ )
+ }
+
+ @Test
+ fun testSecondaryLabelDescription_booleanInactive() {
+ val state = QSTile.BooleanState()
+ state.state = Tile.STATE_INACTIVE
+ state.secondaryLabel = ""
+
+ tileView.changeState(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+ context.getString(R.string.switch_bar_off)
+ )
+ }
+
+ @Test
+ fun testSecondaryLabelDescription_booleanActive() {
+ val state = QSTile.BooleanState()
+ state.state = Tile.STATE_ACTIVE
+ state.secondaryLabel = ""
+
+ tileView.changeState(state)
+
+ assertThat(state.secondaryLabel as CharSequence).isEqualTo(
+ context.getString(R.string.switch_bar_on)
+ )
+ }
+
+ @Test
+ fun testShowCustomDrawableViewBooleanState() {
+ val state = QSTile.BooleanState()
+ state.sideViewCustomDrawable = customDrawable
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowCustomDrawableViewNonBooleanState() {
+ val state = QSTile.State()
+ state.sideViewCustomDrawable = customDrawable
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowCustomDrawableViewBooleanStateForceChevron() {
+ val state = QSTile.BooleanState()
+ state.sideViewCustomDrawable = customDrawable
+ state.forceExpandIcon = true
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun testShowChevronNonBooleanState() {
+ val state = QSTile.State()
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
+ assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testShowChevronBooleanStateForcheShow() {
+ val state = QSTile.BooleanState()
+ state.forceExpandIcon = true
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
+ assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun testNoImageShown() {
+ val state = QSTile.BooleanState()
+
+ tileView.changeState(state)
+
+ assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
+ assertThat(chevronView.visibility).isEqualTo(View.GONE)
+ }
+
+ class FakeTileView(
+ context: Context,
+ icon: QSIconView,
+ collapsed: Boolean
+ ) : QSTileViewImpl(context, icon, collapsed) {
+ fun changeState(state: QSTile.State) {
+ handleStateChanged(state)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 9d463d4..e894b7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -271,7 +271,7 @@
mContext.getString(R.string.wallet_secondary_label_device_locked),
state.secondaryLabel);
assertNotNull(state.stateDescription);
- assertNull(state.sideViewDrawable);
+ assertNull(state.sideViewCustomDrawable);
}
@Test
@@ -287,7 +287,7 @@
"•••• 1234",
state.secondaryLabel);
assertNotNull(state.stateDescription);
- assertNotNull(state.sideViewDrawable);
+ assertNotNull(state.sideViewCustomDrawable);
}
@@ -303,7 +303,7 @@
mContext.getString(R.string.wallet_secondary_label_no_card),
state.secondaryLabel);
assertNotNull(state.stateDescription);
- assertNull(state.sideViewDrawable);
+ assertNull(state.sideViewCustomDrawable);
}
@Test
@@ -315,7 +315,7 @@
assertEquals(Tile.STATE_UNAVAILABLE, state.state);
assertNull(state.stateDescription);
- assertNull(state.sideViewDrawable);
+ assertNull(state.sideViewCustomDrawable);
}
@Test
@@ -342,7 +342,7 @@
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
setUpWalletCard(/* hasCard= */ true);
- assertNotNull(mTile.getState().sideViewDrawable);
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
}
@Test
@@ -362,7 +362,7 @@
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards);
mTestableLooper.processAllMessages();
- assertNotNull(mTile.getState().sideViewDrawable);
+ assertNotNull(mTile.getState().sideViewCustomDrawable);
mTile.handleSetListening(true);
@@ -373,14 +373,14 @@
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards);
mTestableLooper.processAllMessages();
- assertNull(mTile.getState().sideViewDrawable);
+ assertNull(mTile.getState().sideViewCustomDrawable);
}
@Test
public void testQueryCards_noCards_notUpdateSideViewDrawable() {
setUpWalletCard(/* hasCard= */ false);
- assertNull(mTile.getState().sideViewDrawable);
+ assertNull(mTile.getState().sideViewCustomDrawable);
}
@Test
@@ -395,7 +395,7 @@
mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
mTestableLooper.processAllMessages();
- assertNull(mTile.getState().sideViewDrawable);
+ assertNull(mTile.getState().sideViewCustomDrawable);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b2487e8..b3d52b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -178,7 +178,7 @@
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
- @Mock private StatusBarIconController mStatusBarIconController;
+ @Mock private StatusBarSignalPolicy mStatusBarSignalPolicy;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@@ -356,7 +356,7 @@
mLightBarController,
mAutoHideController,
mKeyguardUpdateMonitor,
- mStatusBarIconController,
+ mStatusBarSignalPolicy,
mPulseExpansionHandler,
mNotificationWakeUpCoordinator,
mKeyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
new file mode 100644
index 0000000..0e77bb3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.ongoingcall
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEXT_VIEW_MAX_WIDTH = 400
+
+// When a [Chronometer] is created, it starts off with "00:00" as its text.
+private const val INITIAL_TEXT = "00:00"
+private const val LARGE_TEXT = "00:000"
+private const val XL_TEXT = "00:0000"
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class OngoingCallChronometerTest : SysuiTestCase() {
+
+ private lateinit var textView: OngoingCallChronometer
+ private lateinit var doesNotFitText: String
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ TestableLooper.get(this).runWithLooper {
+ val chipView = LayoutInflater.from(mContext)
+ .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ textView = chipView.findViewById(R.id.ongoing_call_chip_time)!!
+ measureTextView()
+ calculateDoesNotFixText()
+ }
+ }
+
+ @Test
+ fun verifyTextSizes() {
+ val initialTextLength = textView.paint.measureText(INITIAL_TEXT)
+ val largeTextLength = textView.paint.measureText(LARGE_TEXT)
+ val xlTextLength = textView.paint.measureText(XL_TEXT)
+
+ // Assert that our test text sizes do what we expect them to do in the rest of the tests.
+ assertThat(initialTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
+ assertThat(largeTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
+ assertThat(xlTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
+ assertThat(textView.paint.measureText(doesNotFitText)).isGreaterThan(TEXT_VIEW_MAX_WIDTH)
+
+ assertThat(largeTextLength).isGreaterThan(initialTextLength)
+ assertThat(xlTextLength).isGreaterThan(largeTextLength)
+ }
+
+ @Test
+ fun onMeasure_initialTextFitsInSpace_textDisplayed() {
+ assertThat(textView.measuredWidth).isGreaterThan(0)
+ }
+
+ @Test
+ fun onMeasure_newTextLargerThanPreviousText_widthGetsLarger() {
+ val initialTextLength = textView.measuredWidth
+
+ setTextAndMeasure(LARGE_TEXT)
+
+ assertThat(textView.measuredWidth).isGreaterThan(initialTextLength)
+ }
+
+ @Test
+ fun onMeasure_newTextSmallerThanPreviousText_widthDoesNotGetSmaller() {
+ setTextAndMeasure(XL_TEXT)
+ val xlWidth = textView.measuredWidth
+
+ setTextAndMeasure(LARGE_TEXT)
+
+ assertThat(textView.measuredWidth).isEqualTo(xlWidth)
+ }
+
+ @Test
+ fun onMeasure_textDoesNotFit_textHidden() {
+ setTextAndMeasure(doesNotFitText)
+
+ assertThat(textView.measuredWidth).isEqualTo(0)
+ }
+
+ @Test
+ fun onMeasure_newTextFitsButPreviousTextDidNot_textHidden() {
+ setTextAndMeasure(doesNotFitText)
+
+ setTextAndMeasure(LARGE_TEXT)
+
+ assertThat(textView.measuredWidth).isEqualTo(0)
+ }
+
+ @Test
+ fun resetBase_hadLongerTextThenSetBaseThenShorterText_widthIsShort() {
+ setTextAndMeasure(XL_TEXT)
+ val xlWidth = textView.measuredWidth
+
+ textView.base = 0L
+ setTextAndMeasure(INITIAL_TEXT)
+
+ assertThat(textView.measuredWidth).isLessThan(xlWidth)
+ assertThat(textView.measuredWidth).isGreaterThan(0)
+ }
+
+ @Test
+ fun setBase_wasHidingTextThenSetBaseThenShorterText_textShown() {
+ setTextAndMeasure(doesNotFitText)
+
+ textView.base = 0L
+ setTextAndMeasure(INITIAL_TEXT)
+
+ assertThat(textView.measuredWidth).isGreaterThan(0)
+ }
+
+ private fun setTextAndMeasure(text: String) {
+ textView.text = text
+ measureTextView()
+ }
+
+ private fun measureTextView() {
+ textView.measure(
+ View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ }
+
+ /**
+ * Calculates what [doesNotFitText] should be. Needs to be done dynamically because different
+ * devices have different densities, which means the textView can fit different amounts of
+ * characters.
+ */
+ private fun calculateDoesNotFixText() {
+ var currentText = XL_TEXT + "0"
+ while (textView.paint.measureText(currentText) <= TEXT_VIEW_MAX_WIDTH) {
+ currentText += "0"
+ }
+ doesNotFitText = currentText
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index c244290..896e330 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone.ongoingcall
+import android.app.ActivityManager
+import android.app.IActivityManager
+import android.app.IUidObserver
import android.app.Notification
import android.app.PendingIntent
import android.app.Person
@@ -34,31 +37,46 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.*
import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
-import org.mockito.Mockito.verify
import org.mockito.Mockito.never
import org.mockito.Mockito.times
-import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+private const val CALL_UID = 900
+
+// A process state that represents the process being visible to the user.
+private const val PROC_STATE_VISIBLE = ActivityManager.PROCESS_STATE_TOP
+
+// A process state that represents the process being invisible to the user.
+private const val PROC_STATE_INVISIBLE = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class OngoingCallControllerTest : SysuiTestCase() {
+ private val clock = FakeSystemClock()
+ private val mainExecutor = FakeExecutor(clock)
+
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
+ @Mock private lateinit var mockIActivityManager: IActivityManager
private lateinit var chipView: LinearLayout
@@ -76,7 +94,12 @@
val notificationCollection = mock(CommonNotifCollection::class.java)
controller = OngoingCallController(
- notificationCollection, featureFlags, FakeSystemClock(), mockActivityStarter)
+ notificationCollection,
+ featureFlags,
+ clock,
+ mockActivityStarter,
+ mainExecutor,
+ mockIActivityManager)
controller.init()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -84,34 +107,37 @@
val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture())
notifCollectionListener = collectionListenerCaptor.value!!
+
+ `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
+ .thenReturn(PROC_STATE_INVISIBLE)
}
@Test
fun onEntryUpdated_isOngoingCallNotif_listenerNotifiedWithRightCallTime() {
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
- verify(mockOngoingCallListener).onOngoingCallStarted(anyBoolean())
+ verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
@Test
fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
- verify(mockOngoingCallListener, never()).onOngoingCallStarted(anyBoolean())
+ verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
}
@Test
fun onEntryRemoved_ongoingCallNotif_listenerNotified() {
notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED)
- verify(mockOngoingCallListener).onOngoingCallEnded(anyBoolean())
+ verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
@Test
fun onEntryRemoved_notOngoingCallNotif_listenerNotNotified() {
notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
- verify(mockOngoingCallListener, never()).onOngoingCallEnded(anyBoolean())
+ verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
}
@Test
@@ -120,13 +146,26 @@
}
@Test
- fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() {
+ fun hasOngoingCall_ongoingCallNotifSentAndCallAppNotVisible_returnsTrue() {
+ `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
+ .thenReturn(PROC_STATE_INVISIBLE)
+
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
assertThat(controller.hasOngoingCall()).isTrue()
}
@Test
+ fun hasOngoingCall_ongoingCallNotifSentButCallAppVisible_returnsFalse() {
+ `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
+ .thenReturn(PROC_STATE_VISIBLE)
+
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ assertThat(controller.hasOngoingCall()).isFalse()
+ }
+
+ @Test
fun hasOngoingCall_ongoingCallNotifSentButInvalidChipView_returnsFalse() {
val invalidChipView = LinearLayout(context)
controller.setChipView(invalidChipView)
@@ -169,7 +208,52 @@
// Verify the listener was notified once for the initial call and again when the new view
// was set.
- verify(mockOngoingCallListener, times(2)).onOngoingCallStarted(anyBoolean())
+ verify(mockOngoingCallListener, times(2))
+ .onOngoingCallStateChanged(anyBoolean())
+ }
+
+ @Test
+ fun callProcessChangesToVisible_listenerNotified() {
+ // Start the call while the process is invisible.
+ `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
+ .thenReturn(PROC_STATE_INVISIBLE)
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
+ verify(mockIActivityManager).registerUidObserver(
+ captor.capture(), any(), any(), nullable(String::class.java))
+ val uidObserver = captor.value
+
+ // Update the process to visible.
+ uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_VISIBLE, 0, 0)
+ mainExecutor.advanceClockToLast()
+ mainExecutor.runAllReady();
+
+ // Once for when the call was started, and another time when the process visibility changes.
+ verify(mockOngoingCallListener, times(2))
+ .onOngoingCallStateChanged(anyBoolean())
+ }
+
+ @Test
+ fun callProcessChangesToInvisible_listenerNotified() {
+ // Start the call while the process is visible.
+ `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
+ .thenReturn(PROC_STATE_VISIBLE)
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
+ verify(mockIActivityManager).registerUidObserver(
+ captor.capture(), any(), any(), nullable(String::class.java))
+ val uidObserver = captor.value
+
+ // Update the process to invisible.
+ uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_INVISIBLE, 0, 0)
+ mainExecutor.advanceClockToLast()
+ mainExecutor.runAllReady();
+
+ // Once for when the call was started, and another time when the process visibility changes.
+ verify(mockOngoingCallListener, times(2))
+ .onOngoingCallStateChanged(anyBoolean())
}
private fun createOngoingCallNotifEntry(): NotificationEntry {
@@ -179,6 +263,7 @@
val contentIntent = mock(PendingIntent::class.java)
`when`(contentIntent.intent).thenReturn(mock(Intent::class.java))
notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent)
+ notificationEntryBuilder.setUid(CALL_UID)
return notificationEntryBuilder.build()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index f33c9e8..abc66db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -76,6 +76,7 @@
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.telephony.TelephonyListenerManager;
+import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -124,6 +125,7 @@
protected DeviceProvisionedListener mUserCallback;
protected Instrumentation mInstrumentation;
protected DemoModeController mDemoModeController;
+ protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected int mSubId;
@@ -174,6 +176,7 @@
mMockBd = mock(BroadcastDispatcher.class);
mMockNsm = mock(NetworkScoreManager.class);
mMockSubDefaults = mock(SubscriptionDefaults.class);
+ mCarrierConfigTracker = mock(CarrierConfigTracker.class);
mNetCapabilities = new NetworkCapabilities();
when(mMockTm.isDataCapable()).thenReturn(true);
when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm);
@@ -231,7 +234,8 @@
mMockSubDefaults,
mMockProvisionController,
mMockBd,
- mDemoModeController);
+ mDemoModeController,
+ mCarrierConfigTracker);
setupNetworkController();
// Trigger blank callbacks to always get the current state (some tests don't trigger
@@ -298,7 +302,8 @@
mCallbackHandler,
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
- mock(DeviceProvisionedController.class), mMockBd, mDemoModeController);
+ mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
+ mCarrierConfigTracker);
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 6219faf..09554e717 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -21,6 +21,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -111,7 +112,8 @@
mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
- mock(DeviceProvisionedController.class), mMockBd, mDemoModeController);
+ mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
+ mock(CarrierConfigTracker.class));
setupNetworkController();
setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 8d3e403..1e7801d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -41,6 +41,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
+import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,7 +67,7 @@
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController);
+ mDemoModeController, mock(CarrierConfigTracker.class));
setupNetworkController();
verifyLastMobileDataIndicators(false, -1, 0);
@@ -86,7 +87,7 @@
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController);
+ mDemoModeController, mock(CarrierConfigTracker.class));
mNetworkController.registerListeners();
// Wait for the main looper to execute the previous command
@@ -154,7 +155,7 @@
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController);
+ mDemoModeController, mock(CarrierConfigTracker.class));
setupNetworkController();
// No Subscriptions.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index bfb98de..687ca60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -269,9 +269,9 @@
}
// Set the ImsType to be IMS_TYPE_WWAN
setImsType(1);
+ setupDefaultSignal();
for (int testStrength = 0;
testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
- setupDefaultSignal();
setLevel(testStrength);
verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index d922d2b..3bb6e08 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1839,6 +1839,8 @@
// For a full update we replace the RemoteViews completely.
widget.views = views;
}
+ widget.views.setProviderInstanceId(UPDATE_COUNTER.get());
+
int memoryUsage;
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
(widget.views != null) &&
@@ -1939,6 +1941,9 @@
|| widget.host.callbacks == null || widget.host.zombie) {
return;
}
+ if (updateViews != null) {
+ updateViews.setProviderInstanceId(requestId);
+ }
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index c23f1ca..ed25452 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -38,7 +38,7 @@
/**
* Thread pool used during initialization of system server.
*
- * <p>System services can {@link #submit(Runnable)} tasks for execution during boot.
+ * <p>System services can {@link #submit(Runnable, String)} tasks for execution during boot.
* The pool will be shut down after {@link SystemService#PHASE_BOOT_COMPLETED}.
*
* <p>New tasks <em>should not</em> be submitted afterwards.
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 6c81de6..39321bf 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -70,12 +70,20 @@
/** @hide */
protected static final boolean DEBUG_USER = false;
- /*
+ /**
* The earliest boot phase the system send to system services on boot.
*/
public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100;
/**
+ * Boot phase that blocks on SensorService availability. The service gets started
+ * asynchronously since it may take awhile to actually finish initializing.
+ *
+ * @hide
+ */
+ public static final int PHASE_WAIT_FOR_SENSOR_SERVICE = 200;
+
+ /**
* After receiving this boot phase, services can obtain lock settings data.
*/
public static final int PHASE_LOCK_SETTINGS_READY = 480;
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index ab3060a..dc8f84a 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -33,7 +33,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
-import android.app.IOnProjectionStateChangeListener;
+import android.app.IOnProjectionStateChangedListener;
import android.app.IUiModeManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -181,7 +181,7 @@
private SparseArray<List<ProjectionHolder>> mProjectionHolders;
@GuardedBy("mLock")
@Nullable
- private SparseArray<RemoteCallbackList<IOnProjectionStateChangeListener>> mProjectionListeners;
+ private SparseArray<RemoteCallbackList<IOnProjectionStateChangedListener>> mProjectionListeners;
public UiModeManagerService(Context context) {
this(context, /* setupWizardComplete= */ false, /* tm= */ null, new Injector());
@@ -993,11 +993,11 @@
}
}
- public void addOnProjectionStateChangeListener(IOnProjectionStateChangeListener listener,
+ public void addOnProjectionStateChangedListener(IOnProjectionStateChangedListener listener,
@UiModeManager.ProjectionType int projectionType) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PROJECTION_STATE,
- "registerProjectionStateListener");
+ "addOnProjectionStateChangedListener");
if (projectionType == PROJECTION_TYPE_NONE) {
return;
}
@@ -1027,11 +1027,11 @@
}
- public void removeOnProjectionStateChangeListener(
- IOnProjectionStateChangeListener listener) {
+ public void removeOnProjectionStateChangedListener(
+ IOnProjectionStateChangedListener listener) {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PROJECTION_STATE,
- "unregisterProjectionStateListener");
+ "removeOnProjectionStateChangedListener");
synchronized (mLock) {
if (mProjectionListeners != null) {
for (int i = 0; i < mProjectionListeners.size(); ++i) {
@@ -1191,7 +1191,7 @@
// Every listener that is affected must be called back with all the state they are
// listening for.
if ((changedProjectionType & listenerProjectionType) != 0) {
- RemoteCallbackList<IOnProjectionStateChangeListener> listeners =
+ RemoteCallbackList<IOnProjectionStateChangedListener> listeners =
mProjectionListeners.valueAt(i);
List<String> packageNames = new ArrayList<>();
@UiModeManager.ProjectionType int activeProjectionTypes =
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 57254c1..df4c2cfc 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -87,6 +87,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -429,6 +430,7 @@
public void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
// Startup VCN instances
synchronized (mLock) {
+ final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
mLastSnapshot = snapshot;
// Start any VCN instances as necessary
@@ -476,11 +478,29 @@
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
}
}
+
+ final Map<ParcelUuid, Set<Integer>> oldSubGrpMappings =
+ getSubGroupToSubIdMappings(oldSnapshot);
+ final Map<ParcelUuid, Set<Integer>> currSubGrpMappings =
+ getSubGroupToSubIdMappings(mLastSnapshot);
+ if (!currSubGrpMappings.equals(oldSubGrpMappings)) {
+ notifyAllPolicyListenersLocked();
+ }
}
}
}
@GuardedBy("mLock")
+ private Map<ParcelUuid, Set<Integer>> getSubGroupToSubIdMappings(
+ @NonNull TelephonySubscriptionSnapshot snapshot) {
+ final Map<ParcelUuid, Set<Integer>> subGrpMappings = new ArrayMap<>();
+ for (ParcelUuid subGrp : mVcns.keySet()) {
+ subGrpMappings.put(subGrp, snapshot.getAllSubIdsInGroup(subGrp));
+ }
+ return subGrpMappings;
+ }
+
+ @GuardedBy("mLock")
private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
if (vcnToTeardown == null) {
@@ -813,6 +833,8 @@
if (isVcnManagedNetwork) {
ncBuilder.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ } else {
+ ncBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
if (isRestrictedCarrierWifi) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d526ebc..6a3cb6f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7777,16 +7777,25 @@
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
- 0 /* storage_health_code */,
- 0 /* data_loader_status_code */,
- false /* read_logs_enabled */,
- 0 /* millis_since_last_data_loader_bind */,
- 0 /* data_loader_bind_delay_millis */,
- 0 /* total_delayed_reads */,
- 0 /* total_failed_reads */,
- 0 /* last_read_error_uid */,
- 0 /* last_read_error_millis_since */,
- 0 /* last_read_error_code */
+ incrementalMetrics != null ? incrementalMetrics.getStorageHealthStatusCode()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderStatusCode()
+ : -1,
+ incrementalMetrics != null && incrementalMetrics.getReadLogsEnabled(),
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastDataLoaderBind()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderBindDelayMillis()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalDelayedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalFailedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorUid()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastReadError()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorNumber()
+ : 0
);
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
@@ -15831,8 +15840,12 @@
if (initLocale || !mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
+ final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+ bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(),
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_LOCALE_CHANGED, "");
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
- null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
+ null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, SYSTEM_UID,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.USER_ALL);
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index c1b2a9c..5a7e14a 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -433,16 +433,25 @@
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
- 0 /* storage_health_code */,
- 0 /* data_loader_status_code */,
- false /* read_logs_enabled */,
- 0 /* millis_since_last_data_loader_bind */,
- 0 /* data_loader_bind_delay_millis */,
- 0 /* total_delayed_reads */,
- 0 /* total_failed_reads */,
- 0 /* last_read_error_uid */,
- 0 /* last_read_error_millis_since */,
- 0 /* last_read_error_code */);
+ incrementalMetrics != null ? incrementalMetrics.getStorageHealthStatusCode()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderStatusCode()
+ : -1,
+ incrementalMetrics != null && incrementalMetrics.getReadLogsEnabled(),
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastDataLoaderBind()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getDataLoaderBindDelayMillis()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalDelayedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getTotalFailedReads()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorUid()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastReadError()
+ : -1,
+ incrementalMetrics != null ? incrementalMetrics.getLastReadErrorNumber()
+ : 0);
final ProcessRecord parentPr = parentProcess != null
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index cc98abf..af3f658 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3418,6 +3418,11 @@
return;
}
+ if (app.getPid() == 0 && !app.isPendingStart()) {
+ // This process has been killed and its cleanup is done, don't proceed the LRU update.
+ return;
+ }
+
synchronized (mProcLock) {
updateLruProcessLSP(app, client, hasActivity, hasService);
}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 9396241..78ff67a 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -182,12 +182,18 @@
NAMESPACE_APP_HIBERNATION,
ActivityThread.currentApplication().getMainExecutor(),
this::onDeviceConfigChanged);
- getContext().getSystemService(StatsManager.class)
- .setPullAtomCallback(
- FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
- /* metadata */ null, // use default PullAtomMetadata values
- mBackgroundExecutor,
- new StatsPullAtomCallbackImpl());
+ final StatsManager statsManager = getContext().getSystemService(StatsManager.class);
+ final StatsPullAtomCallbackImpl pullAtomCallback = new StatsPullAtomCallbackImpl();
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
+ /* metadata */ null, // use default PullAtomMetadata values
+ mBackgroundExecutor,
+ pullAtomCallback);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.GLOBAL_HIBERNATED_APPS,
+ /* metadata */ null, // use default PullAtomMetadata values
+ mBackgroundExecutor,
+ pullAtomCallback);
}
}
@@ -291,6 +297,7 @@
stateSnapshot.packageName,
userIdSnapshot,
stateSnapshot.hibernated,
+ // TODO(b/187224817): This isn't the expected value right now.
stateSnapshot.lastUnhibernatedMs);
});
List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
@@ -938,23 +945,39 @@
private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
@Override
public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
- if (atomTag != FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS) {
- return StatsManager.PULL_SKIP;
+ if (!isAppHibernationEnabled()
+ && (atomTag == FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS
+ || atomTag == FrameworkStatsLog.GLOBAL_HIBERNATED_APPS)) {
+ return StatsManager.PULL_SUCCESS;
}
- if (isAppHibernationEnabled()) {
- List<UserInfo> userInfos = mUserManager.getAliveUsers();
- final int numUsers = userInfos.size();
- for (int i = 0; i < numUsers; ++i) {
- final int userId = userInfos.get(i).id;
- if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
- data.add(
- FrameworkStatsLog.buildStatsEvent(
- atomTag,
- getHibernatingPackagesForUser(userId).size(),
- userId)
- );
+
+ switch (atomTag) {
+ case FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS:
+ List<UserInfo> userInfos = mUserManager.getAliveUsers();
+ final int numUsers = userInfos.size();
+ for (int i = 0; i < numUsers; ++i) {
+ final int userId = userInfos.get(i).id;
+ if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ getHibernatingPackagesForUser(userId).size(),
+ userId)
+ );
+ }
}
- }
+ break;
+ case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS:
+ int hibernatedAppCount = 0;
+ synchronized (mLock) {
+ for (GlobalLevelState state : mGlobalHibernationStates.values()) {
+ if (state.hibernated) hibernatedAppCount++;
+ }
+ }
+ data.add(FrameworkStatsLog.buildStatsEvent(atomTag, hibernatedAppCount));
+ break;
+ default:
+ return StatsManager.PULL_SKIP;
}
return StatsManager.PULL_SUCCESS;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7eaf18f..99b0d81 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -3065,6 +3065,13 @@
shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation);
}
+ // TODO b/184963112: remove once full blaming is implemented
+ private boolean isRecognitionServiceTemp(int code, String packageName) {
+ return code == OP_RECORD_AUDIO
+ && (packageName.equals("com.google.android.googlequicksearchbox")
+ || packageName.equals("com.google.android.tts"));
+ }
+
private SyncNotedAppOp noteProxyOperationImpl(int code, AttributionSource attributionSource,
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation) {
@@ -3085,13 +3092,15 @@
String resolveProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
proxyPackageName);
if (resolveProxyPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code,
+ proxiedAttributionTag, proxiedPackageName);
}
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame
+ || isRecognitionServiceTemp(code, proxyPackageName);
if (!skipProxyOperation) {
final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
@@ -3101,14 +3110,16 @@
resolveProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null,
proxyFlags, !isProxyTrusted, "proxy " + message, shouldCollectMessage);
if (proxyReturn.getOpMode() != AppOpsManager.MODE_ALLOWED) {
- return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag);
+ return new SyncNotedAppOp(proxyReturn.getOpMode(), code, proxiedAttributionTag,
+ proxiedPackageName);
}
}
String resolveProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
if (resolveProxiedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
+ proxiedPackageName);
}
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
@@ -3135,7 +3146,8 @@
String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
return noteOperationUnchecked(code, uid, resolvedPackageName, attributionTag,
Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF,
@@ -3152,7 +3164,8 @@
bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "noteOperation", e);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
synchronized (this) {
@@ -3163,7 +3176,8 @@
AppOpsManager.MODE_IGNORED);
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
final Op op = getOpLocked(ops, code, uid, true);
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3179,7 +3193,8 @@
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
@@ -3192,7 +3207,7 @@
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
uidMode);
- return new SyncNotedAppOp(uidMode, code, attributionTag);
+ return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
@@ -3205,7 +3220,7 @@
attributedOp.rejected(uidState.state, flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
mode);
- return new SyncNotedAppOp(mode, code, attributionTag);
+ return new SyncNotedAppOp(mode, code, attributionTag, packageName);
}
}
if (DEBUG) {
@@ -3224,7 +3239,8 @@
shouldCollectMessage);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ packageName);
}
}
@@ -3528,7 +3544,8 @@
String resolvedPackageName = AppOpsManager.resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
// As a special case for OP_RECORD_AUDIO_HOTWORD, which we use only for attribution
@@ -3539,7 +3556,7 @@
if (code == OP_RECORD_AUDIO_HOTWORD) {
int result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
if (result != AppOpsManager.MODE_ALLOWED) {
- return new SyncNotedAppOp(result, code, attributionTag);
+ return new SyncNotedAppOp(result, code, attributionTag, packageName);
}
}
return startOperationUnchecked(clientId, code, uid, packageName, attributionTag,
@@ -3578,7 +3595,8 @@
String resolvedProxyPackageName = AppOpsManager.resolvePackageName(proxyUid,
proxyPackageName);
if (resolvedProxyPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
+ proxiedPackageName);
}
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
@@ -3589,7 +3607,8 @@
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
if (resolvedProxiedPackageName == null) {
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, proxiedAttributionTag,
+ proxiedPackageName);
}
final int proxiedFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXIED
@@ -3637,7 +3656,8 @@
bypass = verifyAndGetBypass(uid, packageName, attributionTag);
} catch (SecurityException e) {
Slog.e(TAG, "startOperation", e);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
synchronized (this) {
@@ -3649,7 +3669,8 @@
}
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
- return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
+ packageName);
}
final Op op = getOpLocked(ops, code, uid, true);
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
@@ -3657,7 +3678,8 @@
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, AppOpsManager.MODE_IGNORED);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
+ packageName);
}
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3678,7 +3700,7 @@
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, uidMode);
}
- return new SyncNotedAppOp(uidMode, code, attributionTag);
+ return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
}
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
@@ -3694,7 +3716,7 @@
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, mode);
}
- return new SyncNotedAppOp(mode, code, attributionTag);
+ return new SyncNotedAppOp(mode, code, attributionTag, packageName);
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
@@ -3716,7 +3738,8 @@
message, shouldCollectMessage);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag);
+ return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ packageName);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 741946e..1fcad62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -31,8 +31,8 @@
public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 7f86c62..1edf5af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -26,8 +26,8 @@
public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> {
public RevokeChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
- @NonNull IBinder token, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, null /* listener */, 0 /* userId */, owner,
+ @NonNull IBinder token, int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 5804622..904c399 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -35,8 +35,9 @@
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 84d239e..dd3057e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -300,7 +300,7 @@
mHandler.post(() -> {
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId);
scheduleForSensor(sensorId, client);
});
}
@@ -310,7 +310,7 @@
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mSensors.get(sensorId).getLazySession(), token, opPackageName, sensorId,
+ mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId,
challenge);
scheduleForSensor(sensorId, client);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
index 99bf893..7a69c44 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java
@@ -37,8 +37,8 @@
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId, long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 5dfc590..f908fba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -518,8 +518,8 @@
scheduleUpdateActiveUserWithoutHandler(userId);
final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
- mSensorId, mCurrentChallengeOwner);
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, mCurrentChallengeOwner);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -548,7 +548,7 @@
}
final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
- mLazyDaemon, token, opPackageName, mSensorId);
+ mLazyDaemon, token, userId, opPackageName, mSensorId);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index 24af817..3e0064e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -43,9 +43,9 @@
FaceGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId,
- @Nullable FaceGenerateChallengeClient interruptedClient) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId, @Nullable FaceGenerateChallengeClient interruptedClient) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
mInterruptedClient = interruptedClient;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
index ff3e770..5ec7a98 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java
@@ -35,8 +35,8 @@
FaceRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 15a85e6..293b57d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -37,8 +37,8 @@
@NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token,
@NonNull ClientMonitorCallbackConverter listener,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 083df19..388bd0a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -300,7 +300,7 @@
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
sensorId);
scheduleForSensor(sensorId, client);
});
@@ -313,7 +313,7 @@
final FingerprintRevokeChallengeClient client =
new FingerprintRevokeChallengeClient(mContext,
mSensors.get(sensorId).getLazySession(), token,
- opPackageName, sensorId, challenge);
+ userId, opPackageName, sensorId, challenge);
scheduleForSensor(sensorId, client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
index 90c6978..9e6f1bc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -37,8 +37,8 @@
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId, long challenge) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId, long challenge) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
mChallenge = challenge;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 3528690..f0ea90d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -528,7 +528,7 @@
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+ new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
mSensorProperties.sensorId);
mScheduler.scheduleClientMonitor(client);
});
@@ -539,7 +539,8 @@
@NonNull String opPackageName, long challenge) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
- mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
+ mContext, mLazyDaemon, token, userId, opPackageName,
+ mSensorProperties.sensorId);
mScheduler.scheduleClientMonitor(client);
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 302ec2b..3584397 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -38,8 +38,9 @@
FingerprintGenerateChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, listener, owner, sensorId);
+ @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+ int sensorId) {
+ super(context, lazyDaemon, token, listener, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
index 93d8ff3..b6b29b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
@@ -37,8 +37,8 @@
FingerprintRevokeChallengeClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
- @NonNull String owner, int sensorId) {
- super(context, lazyDaemon, token, owner, sensorId);
+ int userId, @NonNull String owner, int sensorId) {
+ super(context, lazyDaemon, token, userId, owner, sensorId);
}
@Override
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 17f39dd..c3c42ba 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -261,10 +261,12 @@
pw.print(pidCounts.get(pid)); pw.println(" observers");
}
pw.println();
- pw.print(" Total number of nodes: "); pw.println(counts[0]);
- pw.print(" Total number of observers: "); pw.println(counts[1]);
+ pw.increaseIndent();
+ pw.print("Total number of nodes: "); pw.println(counts[0]);
+ pw.print("Total number of observers: "); pw.println(counts[1]);
- sObserverDeathDispatcher.dump(pw, " ");
+ sObserverDeathDispatcher.dump(pw);
+ pw.decreaseIndent();
}
synchronized (sObserverLeakDetectedUid) {
pw.println();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6083bc5..44169a5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -645,7 +645,7 @@
sb.append("Archive (");
sb.append(N);
sb.append(" notification");
- sb.append((N==1)?")":"s)");
+ sb.append((N == 1) ? ")" : "s)");
return sb.toString();
}
@@ -718,6 +718,22 @@
}
}
}
+
+ void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) {
+ synchronized (mBufferLock) {
+ Iterator<Pair<StatusBarNotification, Integer>> iter = descendingIterator();
+ int i = 0;
+ while (iter.hasNext()) {
+ final StatusBarNotification sbn = iter.next().first;
+ if (filter != null && !filter.matches(sbn)) continue;
+ pw.println(" " + sbn);
+ if (++i >= 5) {
+ if (iter.hasNext()) pw.println(" ...");
+ break;
+ }
+ }
+ }
+ }
}
void loadDefaultApprovedServices(int userId) {
@@ -5928,17 +5944,7 @@
+ mPreferencesHelper.shouldHideSilentStatusIcons());
}
pw.println(" mArchive=" + mArchive.toString());
- Iterator<Pair<StatusBarNotification, Integer>> iter = mArchive.descendingIterator();
- int j=0;
- while (iter.hasNext()) {
- final StatusBarNotification sbn = iter.next().first;
- if (filter != null && !filter.matches(sbn)) continue;
- pw.println(" " + sbn);
- if (++j >= 5) {
- if (iter.hasNext()) pw.println(" ...");
- break;
- }
- }
+ mArchive.dumpImpl(pw, filter);
if (!zenOnly) {
N = mEnqueuedNotifications.size();
@@ -8304,6 +8310,21 @@
int rank, int count, boolean wasPosted, String listenerName) {
final String canceledKey = r.getKey();
+ // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
+ // does not already exist, then null will be returned.
+ final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
+ REQUEST_CODE_TIMEOUT,
+ new Intent(ACTION_NOTIFICATION_TIMEOUT)
+ .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+ .appendPath(r.getKey()).build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
+
+ // Cancel alarm corresponding to pi.
+ if (pi != null) {
+ mAlarmManager.cancel(pi);
+ }
+
// Record caller.
recordCallerLocked(r);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index db2b166..219fa3c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -134,7 +134,6 @@
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
-import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
import android.Manifest;
import android.annotation.AppIdInt;
@@ -2516,8 +2515,8 @@
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
- PackageSetting ps = a == null ? null : mSettings.getPackageLPr(a.getPackageName());
if (pkg != null && mSettings.isEnabledAndMatchLPr(pkg, a, flags, userId)) {
+ PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
if (ps == null) return null;
if (shouldFilterApplicationLocked(
ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
@@ -2527,8 +2526,8 @@
a, flags, ps.readUserState(userId), userId, ps);
}
if (resolveComponentName().equals(component)) {
- return generateDelegateActivityInfo(pkg, ps, new PackageUserState(),
- mResolveActivity, flags, userId);
+ return PackageParser.generateActivityInfo(
+ mResolveActivity, flags, new PackageUserState(), userId);
}
return null;
}
@@ -3174,8 +3173,8 @@
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
- ephemeralInstaller.activityInfo = generateDelegateActivityInfo(ps.getPkg(), ps,
- ps.readUserState(userId), instantAppInstallerActivity(), 0 /*flags*/, userId);
+ ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
+ instantAppInstallerActivity(), 0, ps.readUserState(userId), userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
@@ -3259,7 +3258,7 @@
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
pi.applicationInfo =
- PackageInfoUtils.generateApplicationInfo(p, flags, state, userId, ps);
+ PackageParser.generateApplicationInfo(ai, flags, state, userId);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ ps.name + "]. Provides a minimum info.");
@@ -3375,19 +3374,6 @@
return getInstalledPackagesBody(flags, userId, callingUid);
}
- private static ActivityInfo generateDelegateActivityInfo(@Nullable AndroidPackage pkg,
- @Nullable PackageSetting ps, @NonNull PackageUserState state,
- @Nullable ActivityInfo activity, int flags, int userId) {
- if (activity == null || pkg == null
- || !checkUseInstalledOrHidden(pkg, ps, state, flags)) {
- return null;
- }
- final ActivityInfo info = new ActivityInfo(activity);
- info.applicationInfo =
- PackageInfoUtils.generateApplicationInfo(pkg, flags, state, userId, ps);
- return info;
- }
-
public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
// writer
@@ -23672,7 +23658,7 @@
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
- ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);
+ PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
if (DEBUG_SETTINGS) {
Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index b89dbdc..61f51e3 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -417,7 +417,7 @@
* Returns true if the package is installed and not hidden, or if the caller
* explicitly wanted all uninstalled and hidden packages as well.
*/
- public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
+ private static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
PackageSetting pkgSetting, PackageUserState state,
@PackageManager.PackageInfoFlags int flags) {
// Returns false if the package is hidden system app until installed.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 884bbea..1bfa72f 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -70,7 +70,9 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
+import android.content.AttributionSourceState;
import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
@@ -104,6 +106,7 @@
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.permission.IOnPermissionsChangeListener;
+import android.permission.IPermissionChecker;
import android.permission.IPermissionManager;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
@@ -164,6 +167,7 @@
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -451,6 +455,7 @@
if (permissionService == null) {
permissionService = new PermissionManagerService(context, availableFeatures);
ServiceManager.addService("permissionmgr", permissionService);
+ ServiceManager.addService("permission_checker", new PermissionCheckerService(context));
}
return LocalServices.getService(PermissionManagerServiceInternal.class);
}
@@ -2175,23 +2180,30 @@
}
final int callingUid = Binder.getCallingUid();
- final int userId = UserHandle.getUserId(newPackage.getUid());
- int numRequestedPermissions = newPackage.getRequestedPermissions().size();
- for (int i = 0; i < numRequestedPermissions; i++) {
- PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i),
- newPackage.getPackageName(), 0);
- if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
- continue;
+ for (int userId: getAllUserIds()) {
+ int numRequestedPermissions = newPackage.getRequestedPermissions().size();
+ for (int i = 0; i < numRequestedPermissions; i++) {
+ PermissionInfo permInfo = getPermissionInfo(
+ newPackage.getRequestedPermissions().get(i),
+ newPackage.getPackageName(), 0);
+ if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
+ continue;
+ }
+
+ EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
+ "Revoking permission " + permInfo.name + " from package "
+ + newPackage.getPackageName() + " as either the sdk downgraded "
+ + downgradedSdk + " or newly requested legacy full storage "
+ + newlyRequestsLegacy);
+
+ try {
+ revokeRuntimePermissionInternal(newPackage.getPackageName(), permInfo.name,
+ false, callingUid, userId, null, mDefaultPermissionCallback);
+ } catch (IllegalStateException | SecurityException e) {
+ Log.e(TAG, "unable to revoke " + permInfo.name + " for "
+ + newPackage.getPackageName() + " user " + userId, e);
+ }
}
-
- EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
- "Revoking permission " + permInfo.name + " from package "
- + newPackage.getPackageName() + " as either the sdk downgraded "
- + downgradedSdk + " or newly requested legacy full storage "
- + newlyRequestsLegacy);
-
- revokeRuntimePermissionInternal(newPackage.getPackageName(), permInfo.name,
- false, callingUid, userId, null, mDefaultPermissionCallback);
}
}
@@ -5448,4 +5460,419 @@
}
}
}
+
+ /**
+ * TODO: We need to consolidate these APIs either on PermissionManager or an extension
+ * object or a separate PermissionChecker service in context. The impartant part is to
+ * keep a single impl that is exposed to Java and native. We are not sure about the
+ * API shape so let is soak a bit.
+ */
+ private static final class PermissionCheckerService extends IPermissionChecker.Stub {
+ // Cache for platform defined runtime permissions to avoid multi lookup (name -> info)
+ private static final ConcurrentHashMap<String, PermissionInfo> sPlatformPermissions
+ = new ConcurrentHashMap<>();
+
+ private final @NonNull Context mContext;
+ private final @NonNull AppOpsManager mAppOpsManager;
+
+ PermissionCheckerService(@NonNull Context context) {
+ mContext = context;
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ }
+
+ @Override
+ @PermissionChecker.PermissionResult
+ public int checkPermission(@NonNull String permission,
+ @NonNull AttributionSourceState attributionSourceState, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ Objects.requireNonNull(permission);
+ Objects.requireNonNull(attributionSourceState);
+ final AttributionSource attributionSource = new AttributionSource(
+ attributionSourceState);
+ final int result = checkPermission(mContext, permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource);
+ // Finish any started op if some step in the attribution chain failed.
+ if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED) {
+ finishDataDelivery(AppOpsManager.permissionToOp(permission),
+ attributionSource.asState());
+ }
+ return result;
+ }
+
+ @Override
+ public void finishDataDelivery(@NonNull String op,
+ @NonNull AttributionSourceState attributionSourceState) {
+ if (op == null || attributionSourceState.packageName == null) {
+ return;
+ }
+ mAppOpsManager.finishProxyOp(op, new AttributionSource(attributionSourceState));
+ if (attributionSourceState.next != null) {
+ finishDataDelivery(op, attributionSourceState.next[0]);
+ }
+ }
+
+ @Override
+ @PermissionChecker.PermissionResult
+ public int checkOp(int op, AttributionSourceState attributionSource,
+ String message, boolean forDataDelivery, boolean startDataDelivery) {
+ int result = checkOp(mContext, op, new AttributionSource(attributionSource), message,
+ forDataDelivery, startDataDelivery);
+ if (result != PermissionChecker.PERMISSION_GRANTED && startDataDelivery) {
+ // Finish any started op if some step in the attribution chain failed.
+ finishDataDelivery(AppOpsManager.opToName(op), attributionSource);
+ }
+ return result;
+ }
+
+ @PermissionChecker.PermissionResult
+ private static int checkPermission(@NonNull Context context, @NonNull String permission,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource) {
+ PermissionInfo permissionInfo = sPlatformPermissions.get(permission);
+
+ if (permissionInfo == null) {
+ try {
+ permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0);
+ if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) {
+ // Double addition due to concurrency is fine - the backing
+ // store is concurrent.
+ sPlatformPermissions.put(permission, permissionInfo);
+ }
+ } catch (PackageManager.NameNotFoundException ignored) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ }
+
+ if (permissionInfo.isAppOp()) {
+ return checkAppOpPermission(context, permission, attributionSource, message,
+ forDataDelivery, fromDatasource);
+ }
+ if (permissionInfo.isRuntime()) {
+ return checkRuntimePermission(context, permission, attributionSource, message,
+ forDataDelivery, startDataDelivery, fromDatasource);
+ }
+
+ if (!fromDatasource && !checkPermission(context, permission, attributionSource.getUid(),
+ attributionSource.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ if (attributionSource.getNext() != null) {
+ return checkPermission(context, permission,
+ attributionSource.getNext(), message, forDataDelivery,
+ startDataDelivery, /*fromDatasource*/ false);
+ }
+
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ @PermissionChecker.PermissionResult
+ private static int checkAppOpPermission(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean fromDatasource) {
+ final int op = AppOpsManager.permissionToOpCode(permission);
+ if (op < 0) {
+ Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!");
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (!(fromDatasource && current == attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current == attributionSource && next != null && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks,
+ selfAccess, singleReceiverFromDatasource);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_IGNORED:
+ case AppOpsManager.MODE_ERRORED: {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_DEFAULT: {
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ attributionSource.getUid(), attributionSource
+ .getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static int checkRuntimePermission(@NonNull Context context,
+ @NonNull String permission, @NonNull AttributionSource attributionSource,
+ @Nullable String message, boolean forDataDelivery, boolean startDataDelivery,
+ boolean fromDatasource) {
+ // Now let's check the identity chain...
+ final int op = AppOpsManager.permissionToOpCode(permission);
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (fromDatasource || next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (!(fromDatasource && current == attributionSource)
+ && next != null && !current.isTrusted(context)) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ // If we already checked the permission for this one, skip the work
+ if (!skipCurrentChecks && !checkPermission(context, permission,
+ current.getUid(), current.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ if (next != null && !checkPermission(context, permission,
+ next.getUid(), next.getRenouncedPermissions())) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ if (op < 0) {
+ // Bg location is one-off runtime modifier permission and has no app op
+ if (sPlatformPermissions.contains(permission)
+ && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) {
+ Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
+ + " with no app op defined!");
+ }
+ if (next == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+ current = next;
+ continue;
+ }
+
+ // The access is for oneself if this is the single receiver of data
+ // after the data source or if this is the single attribution source
+ // in the chain if not from a datasource.
+ final boolean singleReceiverFromDatasource = (fromDatasource
+ && current == attributionSource && next != null && next.getNext() == null);
+ final boolean selfAccess = singleReceiverFromDatasource || next == null;
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ singleReceiverFromDatasource);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static boolean checkPermission(@NonNull Context context, @NonNull String permission,
+ int uid, @NonNull Set<String> renouncedPermissions) {
+ final boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
+ uid) == PackageManager.PERMISSION_GRANTED;
+ if (permissionGranted && renouncedPermissions.contains(permission)
+ && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
+ /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ return permissionGranted;
+ }
+
+ private static int checkOp(@NonNull Context context, @NonNull int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery) {
+ if (op < 0 || attributionSource.getPackageName() == null) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ AttributionSource current = attributionSource;
+ AttributionSource next = null;
+
+ while (true) {
+ final boolean skipCurrentChecks = (next != null);
+ next = current.getNext();
+
+ // If the call is from a datasource we need to vet only the chain before it. This
+ // way we can avoid the datasource creating an attribution context for every call.
+ if (next != null && !current.isTrusted(context)) {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+
+ // The access is for oneself if this is the single attribution source in the chain.
+ final boolean selfAccess = (next == null);
+
+ final int opMode = performOpTransaction(context, op, current, message,
+ forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess,
+ /*fromDatasource*/ false);
+
+ switch (opMode) {
+ case AppOpsManager.MODE_ERRORED: {
+ return PermissionChecker.PERMISSION_HARD_DENIED;
+ }
+ case AppOpsManager.MODE_IGNORED: {
+ return PermissionChecker.PERMISSION_SOFT_DENIED;
+ }
+ }
+
+ if (next == null || next.getNext() == null) {
+ return PermissionChecker.PERMISSION_GRANTED;
+ }
+
+ current = next;
+ }
+ }
+
+ private static int performOpTransaction(@NonNull Context context, int op,
+ @NonNull AttributionSource attributionSource, @Nullable String message,
+ boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation,
+ boolean selfAccess, boolean singleReceiverFromDatasource) {
+ // We cannot perform app ops transactions without a package name. In all relevant
+ // places we pass the package name but just in case there is a bug somewhere we
+ // do a best effort to resolve the package from the UID (pick first without a loss
+ // of generality - they are in the same security sandbox).
+ final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ final AttributionSource accessorSource = (!singleReceiverFromDatasource)
+ ? attributionSource : attributionSource.getNext();
+ if (!forDataDelivery) {
+ final String resolvedAccessorPackageName = resolvePackageName(context,
+ accessorSource);
+ if (resolvedAccessorPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op,
+ accessorSource.getUid(), resolvedAccessorPackageName);
+ final AttributionSource next = accessorSource.getNext();
+ if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) {
+ final String resolvedNextPackageName = resolvePackageName(context, next);
+ if (resolvedNextPackageName == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(),
+ resolvedNextPackageName);
+ }
+ return opMode;
+ } else if (startDataDelivery) {
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ // If the datasource is not in a trusted platform component then in would not
+ // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+ // an app is exposing runtime permission protected data but cannot blame others
+ // in a trusted way which would not properly show in permission usage UIs.
+ // As a fallback we note a proxy op that blames the app and the datasource.
+ try {
+ return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ /*startIfModeDefault*/ false,
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ + " platform defined runtime permission "
+ + AppOpsManager.opToPermission(op) + " while not having "
+ + Manifest.permission.UPDATE_APP_OPS_STATS);
+ return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ final AttributionSource resolvedAttributionSource = resolveAttributionSource(
+ context, accessorSource);
+ if (resolvedAttributionSource.getPackageName() == null) {
+ return AppOpsManager.MODE_ERRORED;
+ }
+ if (selfAccess) {
+ // If the datasource is not in a trusted platform component then in would not
+ // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+ // an app is exposing runtime permission protected data but cannot blame others
+ // in a trusted way which would not properly show in permission usage UIs.
+ // As a fallback we note a proxy op that blames the app and the datasource.
+ try {
+ return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+ resolvedAttributionSource.getPackageName(),
+ resolvedAttributionSource.getAttributionTag(),
+ message);
+ } catch (SecurityException e) {
+ Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+ + " platform defined runtime permission "
+ + AppOpsManager.opToPermission(op) + " while not having "
+ + Manifest.permission.UPDATE_APP_OPS_STATS);
+ return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
+ skipProxyOperation);
+ }
+ } else {
+ return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
+ skipProxyOperation);
+ }
+ }
+ }
+
+ private static @Nullable String resolvePackageName(@NonNull Context context,
+ @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource.getPackageName();
+ }
+ final String[] packageNames = context.getPackageManager().getPackagesForUid(
+ attributionSource.getUid());
+ if (packageNames != null) {
+ // This is best effort if the caller doesn't pass a package. The security
+ // sandbox is UID, therefore we pick an arbitrary package.
+ return packageNames[0];
+ }
+ // Last resort to handle special UIDs like root, etc.
+ return AppOpsManager.resolvePackageName(attributionSource.getUid(),
+ attributionSource.getPackageName());
+ }
+
+ private static @NonNull AttributionSource resolveAttributionSource(
+ @NonNull Context context, @NonNull AttributionSource attributionSource) {
+ if (attributionSource.getPackageName() != null) {
+ return attributionSource;
+ }
+ return attributionSource.withPackageName(resolvePackageName(context,
+ attributionSource));
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 03584b9..68e7bdb 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -256,7 +256,7 @@
private final ContentResolver mContentResolver;
private final BatterySavingStats mBatterySavingStats;
- private final UiModeManager.OnProjectionStateChangeListener mOnProjectionStateChangeListener =
+ private final UiModeManager.OnProjectionStateChangedListener mOnProjectionStateChangedListener =
(t, pkgs) -> mAutomotiveProjectionActive.update(!pkgs.isEmpty());
@GuardedBy("mLock")
@@ -292,8 +292,8 @@
mAccessibilityEnabled.initialize(acm.isEnabled());
UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class);
- uiModeManager.addOnProjectionStateChangeListener(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE,
- mContext.getMainExecutor(), mOnProjectionStateChangeListener);
+ uiModeManager.addOnProjectionStateChangedListener(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE,
+ mContext.getMainExecutor(), mOnProjectionStateChangedListener);
mAutomotiveProjectionActive.initialize(
uiModeManager.getActiveProjectionTypes() != UiModeManager.PROJECTION_TYPE_NONE);
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
new file mode 100644
index 0000000..f7ed8e7
--- /dev/null
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.sensors;
+
+import android.content.Context;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ConcurrentUtils;
+import com.android.server.SystemServerInitThreadPool;
+import com.android.server.SystemService;
+import com.android.server.utils.TimingsTraceAndSlog;
+
+import java.util.concurrent.Future;
+
+public class SensorService extends SystemService {
+ private static final String START_NATIVE_SENSOR_SERVICE = "StartNativeSensorService";
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private Future<?> mSensorServiceStart;
+
+
+ /** Start the sensor service. This is a blocking call and can take time. */
+ private static native void startNativeSensorService();
+
+ public SensorService(Context ctx) {
+ super(ctx);
+ synchronized (mLock) {
+ mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
+ TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+ traceLog.traceBegin(START_NATIVE_SENSOR_SERVICE);
+ startNativeSensorService();
+ traceLog.traceEnd();
+ }, START_NATIVE_SENSOR_SERVICE);
+ }
+ }
+
+ @Override
+ public void onStart() { }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE) {
+ ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart,
+ START_NATIVE_SENSOR_SERVICE);
+ synchronized (mLock) {
+ mSensorServiceStart = null;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 6e0efbf..ba18893 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -488,6 +488,12 @@
public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
/**
+ * Animation when a reveal starting window animation is applied to app window.
+ * @hide
+ */
+ public static final int ANIMATION_TYPE_STARTING_REVEAL = 1 << 7;
+
+ /**
* Bitmask to include all animation types. This is NOT an {@link AnimationType}
* @hide
*/
@@ -505,7 +511,8 @@
ANIMATION_TYPE_RECENTS,
ANIMATION_TYPE_WINDOW_ANIMATION,
ANIMATION_TYPE_INSETS_CONTROL,
- ANIMATION_TYPE_FIXED_TRANSFORM
+ ANIMATION_TYPE_FIXED_TRANSFORM,
+ ANIMATION_TYPE_STARTING_REVEAL
})
@Retention(RetentionPolicy.SOURCE)
@interface AnimationType {}
@@ -523,6 +530,7 @@
case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation";
case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation";
case ANIMATION_TYPE_FIXED_TRANSFORM: return "fixed_rotation";
+ case ANIMATION_TYPE_STARTING_REVEAL: return "starting_reveal";
default: return "unknown type:" + type;
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2c592d0..e5e1a7a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4309,21 +4309,11 @@
// the screen are opaque.
return TASK_VISIBILITY_INVISIBLE;
}
- if (gotRootSplitScreenTask) {
- if (isAssistantType) {
- // Assistant stack can't be visible behind split-screen. In addition to this not
- // making sense, it also works around an issue here we boost the z-order of the
- // assistant window surfaces in window manager whenever it is visible.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (other.isHomeOrRecentsRootTask()) {
- // While in split mode, home task will be reparented to the secondary split and
- // leaving tasks not supporting split below. Due to
- // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
- // the bottom, this makes sure those tasks below home is invisible and won't
- // occlude home task unexpectedly.
- return TASK_VISIBILITY_INVISIBLE;
- }
+ if (isAssistantType && gotRootSplitScreenTask) {
+ // Assistant stack can't be visible behind split-screen. In addition to this not
+ // making sense, it also works around an issue here we boost the z-order of the
+ // assistant window surfaces in window manager whenever it is visible.
+ return TASK_VISIBILITY_INVISIBLE;
}
if (other.mAdjacentTask != null) {
if (adjacentTasks.contains(other.mAdjacentTask)) {
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index ccc0916..fb481b4 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -22,6 +22,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
@@ -38,6 +39,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
@@ -134,29 +136,72 @@
}
}
- void removeStartingWindow(Task task, boolean prepareAnimation) {
- SurfaceControl firstWindowLeash = null;
- Rect mainFrame = null;
- // TODO enable shift up animation once we fix flicker test
-// final boolean playShiftUpAnimation = !task.inMultiWindowMode();
-// if (prepareAnimation && playShiftUpAnimation) {
-// final ActivityRecord topActivity = task.topActivityWithStartingWindow();
-// if (topActivity != null) {
-// final WindowState mainWindow =
-// topActivity.findMainWindow(false/* includeStartingApp */);
-// if (mainWindow != null) {
- // TODO create proper leash instead of the copied SC
-// firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
-// "TaskOrganizerController.removeStartingWindow");
-// mainFrame = mainWindow.getRelativeFrame();
-// }
-// }
-// }
- try {
- mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
- /* TODO(183004107) Revert this when jankiness is solved
- prepareAnimation); */ false);
+ // Capture the animation surface control for activity's main window
+ private class StartingWindowAnimationAdaptor implements AnimationAdapter {
+ private SurfaceControl mAnimationLeash;
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ mAnimationLeash = animationLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (mAnimationLeash == animationLeash) {
+ mAnimationLeash = null;
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 0;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
+ pw.print(mAnimationLeash);
+ pw.println();
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ }
+ }
+
+ void removeStartingWindow(Task task, boolean prepareAnimation) {
+ SurfaceControl windowAnimationLeash = null;
+ Rect mainFrame = null;
+ final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+ if (prepareAnimation && playShiftUpAnimation) {
+ final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+ if (topActivity != null) {
+ final WindowState mainWindow =
+ topActivity.findMainWindow(false/* includeStartingApp */);
+ if (mainWindow != null) {
+ final StartingWindowAnimationAdaptor adaptor =
+ new StartingWindowAnimationAdaptor();
+ final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
+ mainWindow.startAnimation(t, adaptor, false,
+ ANIMATION_TYPE_STARTING_REVEAL);
+ windowAnimationLeash = adaptor.mAnimationLeash;
+ mainFrame = mainWindow.getRelativeFrame();
+ }
+ }
+ }
+ try {
+ mTaskOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
+ mainFrame, prepareAnimation);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2e8d4cd..b40223f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -133,6 +133,7 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -2410,6 +2411,18 @@
ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
}
+ if (startingWindow && StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ // cancel the remove starting window animation on shell
+ if (mActivityRecord != null) {
+ final WindowState mainWindow =
+ mActivityRecord.findMainWindow(false/* includeStartingApp */);
+ if (mainWindow != null && mainWindow.isSelfAnimating(0 /* flags */,
+ ANIMATION_TYPE_STARTING_REVEAL)) {
+ mainWindow.cancelAnimation();
+ }
+ }
+ }
+
ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
System.identityHashCode(mClient.asBinder()),
mWinAnimator.mSurfaceController,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4ce9591..53401fd 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -70,6 +70,7 @@
"com_android_server_PersistentDataBlockService.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
+ "com_android_server_sensor_SensorService.cpp",
"onload.cpp",
":lib_cachedAppOptimizer_native",
":lib_networkStatsFactory_native",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index fb664ab..6721ecf 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -32,7 +32,6 @@
#include <memtrackproxy/MemtrackProxy.h>
#include <schedulerservice/SchedulingPolicyService.h>
-#include <sensorservice/SensorService.h>
#include <sensorservicehidl/SensorManager.h>
#include <stats/StatsAidl.h>
#include <stats/StatsHal.h>
@@ -41,12 +40,10 @@
#include <bionic/reserved_signals.h>
#include <android-base/properties.h>
-#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include <utils/AndroidThreads.h>
-using android::base::GetIntProperty;
using namespace std::chrono_literals;
namespace {
@@ -81,15 +78,6 @@
startStatsAidlService();
}
-static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
- char propBuf[PROPERTY_VALUE_MAX];
- property_get("system_init.startsensorservice", propBuf, "1");
- if (strcmp(propBuf, "1") == 0) {
- SensorService::publish(false /* allowIsolated */,
- IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
- }
-}
-
static void android_server_SystemServer_startMemtrackProxyService(JNIEnv* env,
jobject /* clazz */) {
using aidl::android::hardware::memtrack::MemtrackProxy;
@@ -163,7 +151,6 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"startIStatsService", "()V", (void*)android_server_SystemServer_startIStatsService},
- {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
{"startMemtrackProxyService", "()V",
(void*)android_server_SystemServer_startMemtrackProxyService},
{"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices},
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
new file mode 100644
index 0000000..acad1bc
--- /dev/null
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "SensorService"
+
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#include <cutils/properties.h>
+#include <sensorservice/SensorService.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+namespace android {
+
+static void startNativeSensorService(JNIEnv* env, jclass clazz) {
+ char propBuf[PROPERTY_VALUE_MAX];
+ property_get("system_init.startsensorservice", propBuf, "1");
+ if (strcmp(propBuf, "1") == 0) {
+ SensorService::publish(false /* allowIsolated */,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
+ }
+}
+
+static const JNINativeMethod methods[] = {
+ {"startNativeSensorService", "()V", (void*)startNativeSensorService},
+};
+
+int register_android_server_sensor_SensorService(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
+ NELEM(methods));
+}
+
+}; // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index b043e64..b8961d5 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -62,6 +62,7 @@
int register_android_server_FaceService(JNIEnv* env);
int register_android_server_GpuService(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
+int register_android_server_sensor_SensorService(JNIEnv* env);
};
using namespace android;
@@ -117,5 +118,6 @@
register_android_server_FaceService(env);
register_android_server_GpuService(env);
register_android_server_stats_pull_StatsPullAtomService(env);
+ register_android_server_sensor_SensorService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8dc5011..1e4b248 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -179,6 +179,7 @@
import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService;
+import com.android.server.sensors.SensorService;
import com.android.server.signedconfig.SignedConfigService;
import com.android.server.soundtrigger.SoundTriggerService;
import com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareService;
@@ -427,7 +428,6 @@
private final long mRuntimeStartElapsedTime;
private final long mRuntimeStartUptime;
- private static final String START_SENSOR_SERVICE = "StartSensorService";
private static final String START_HIDL_SERVICES = "StartHidlServices";
private static final String START_BLOB_STORE_SERVICE = "startBlobStoreManagerService";
@@ -435,7 +435,6 @@
private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed";
private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime";
- private Future<?> mSensorServiceStart;
private Future<?> mZygotePreload;
private Future<?> mBlobStoreServiceStart;
@@ -449,9 +448,6 @@
/** Start the IStats services. This is a blocking call and can take time. */
private static native void startIStatsService();
- /** Start the sensor service. This is a blocking call and can take time. */
- private static native void startSensorService();
-
/**
* Start the memtrack proxy service.
*/
@@ -1229,15 +1225,9 @@
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
- // Start sensor service in a separate thread. Completion should be checked
- // before using it.
- mSensorServiceStart = SystemServerInitThreadPool.submit(() -> {
- TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
- traceLog.traceBegin(START_SENSOR_SERVICE);
- startSensorService();
- traceLog.traceEnd();
- }, START_SENSOR_SERVICE);
-
+ t.traceBegin("StartSensorService");
+ mSystemServiceManager.startService(SensorService.class);
+ t.traceEnd();
t.traceEnd(); // startBootstrapServices
}
@@ -1474,8 +1464,7 @@
t.traceBegin("StartWindowManagerService");
// WMS needs sensor service ready
- ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
- mSensorServiceStart = null;
+ mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
@@ -1493,8 +1482,8 @@
t.traceEnd();
// Start receiving calls from HIDL services. Start in in a separate thread
- // because it need to connect to SensorManager. This have to start
- // after START_SENSOR_SERVICE is done.
+ // because it need to connect to SensorManager. This has to start
+ // after PHASE_WAIT_FOR_SENSOR_SERVICE is done.
SystemServerInitThreadPool.submit(() -> {
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(START_HIDL_SERVICES);
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 8382ff4..7234281 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -129,9 +129,11 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -369,6 +371,7 @@
.mockStatic(MetricsHelper.class)
.mockStatic(Settings.Global.class)
.mockStatic(ServiceManager.class)
+ .mockStatic(SystemProperties.class)
.spyStatic(UserHandle.class)
.strictness(Strictness.WARN)
.startMocking();
@@ -2579,6 +2582,24 @@
verify(() -> MetricsHelper.pushAlarmBatchDelivered(10, 5));
}
+ @Test
+ public void setTimeZoneImpl() {
+ final long durationMs = 20000L;
+ when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs);
+ mService.setTimeZoneImpl("UTC");
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mMockContext).sendBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL),
+ isNull(), bundleCaptor.capture());
+ assertEquals(Intent.ACTION_TIMEZONE_CHANGED, intentCaptor.getValue().getAction());
+ final BroadcastOptions bOptions = new BroadcastOptions(bundleCaptor.getValue());
+ assertEquals(durationMs, bOptions.getTemporaryAppAllowlistDuration());
+ assertEquals(TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ bOptions.getTemporaryAppAllowlistType());
+ assertEquals(PowerExemptionManager.REASON_TIMEZONE_CHANGED,
+ bOptions.getTemporaryAppAllowlistReasonCode());
+ }
+
@After
public void tearDown() {
if (mMockingSession != null) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 41237c8..12e0d8b 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -168,7 +168,7 @@
private void mockNoteOperation() {
SyncNotedAppOp allowed = new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED,
- AppOpsManager.OP_GET_USAGE_STATS, null);
+ AppOpsManager.OP_GET_USAGE_STATS, null, mContext.getPackageName());
when(mAppOpsService.noteOperation(eq(AppOpsManager.OP_GET_USAGE_STATS), eq(Process.myUid()),
nullable(String.class), nullable(String.class), any(Boolean.class),
nullable(String.class), any(Boolean.class))).thenReturn(allowed);
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 81c237b..4b3771b 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -55,7 +55,7 @@
import android.Manifest;
import android.app.AlarmManager;
-import android.app.IOnProjectionStateChangeListener;
+import android.app.IOnProjectionStateChangedListener;
import android.app.IUiModeManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -669,46 +669,46 @@
}
@Test
- public void addOnProjectionStateChangeListener_enforcesReadProjStatePermission() {
+ public void addOnProjectionStateChangedListener_enforcesReadProjStatePermission() {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.READ_PROJECTION_STATE), any());
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
- assertThrows(SecurityException.class, () -> mService.addOnProjectionStateChangeListener(
+ assertThrows(SecurityException.class, () -> mService.addOnProjectionStateChangedListener(
listener, PROJECTION_TYPE_ALL));
}
@Test
- public void addOnProjectionStateChangeListener_callsListenerIfProjectionActive()
+ public void addOnProjectionStateChangedListener_callsListenerIfProjectionActive()
throws Exception {
when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
when(listener.asBinder()).thenReturn(mBinder); // Any binder will do
- mService.addOnProjectionStateChangeListener(listener, PROJECTION_TYPE_ALL);
+ mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_AUTOMOTIVE),
eq(List.of(PACKAGE_NAME)));
}
@Test
- public void removeOnProjectionStateChangeListener_enforcesReadProjStatePermission() {
+ public void removeOnProjectionStateChangedListener_enforcesReadProjStatePermission() {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.READ_PROJECTION_STATE), any());
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
- assertThrows(SecurityException.class, () -> mService.removeOnProjectionStateChangeListener(
+ assertThrows(SecurityException.class, () -> mService.removeOnProjectionStateChangedListener(
listener));
}
@Test
- public void removeOnProjectionStateChangeListener() throws Exception {
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ public void removeOnProjectionStateChangedListener() throws Exception {
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
- mService.addOnProjectionStateChangeListener(listener, PROJECTION_TYPE_ALL);
+ mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
- mService.removeOnProjectionStateChangeListener(listener);
+ mService.removeOnProjectionStateChangedListener(listener);
// Now set automotive projection, should not call back.
when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
@@ -716,10 +716,10 @@
}
@Test
- public void projectionStateChangeListener_calledWhenStateChanges() throws Exception {
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ public void projectionStateChangedListener_calledWhenStateChanges() throws Exception {
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
- mService.addOnProjectionStateChangeListener(listener, PROJECTION_TYPE_ALL);
+ mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
verify(listener, atLeastOnce()).asBinder(); // Called twice during register.
// No calls initially, no projection state set.
@@ -748,19 +748,19 @@
}
@Test
- public void projectionStateChangeListener_calledForAnyRelevantStateChange() throws Exception {
+ public void projectionStateChangedListener_calledForAnyRelevantStateChange() throws Exception {
int fakeProjectionType = 0x0002;
int otherFakeProjectionType = 0x0004;
String otherPackageName = "Internet Arms";
when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
when(mPackageManager.getPackageUid(otherPackageName, 0))
.thenReturn(TestInjector.CALLING_UID);
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
- IOnProjectionStateChangeListener listener2 = mock(IOnProjectionStateChangeListener.class);
+ IOnProjectionStateChangedListener listener2 = mock(IOnProjectionStateChangedListener.class);
when(listener2.asBinder()).thenReturn(mBinder); // Any binder will do.
- mService.addOnProjectionStateChangeListener(listener, fakeProjectionType);
- mService.addOnProjectionStateChangeListener(listener2,
+ mService.addOnProjectionStateChangedListener(listener, fakeProjectionType);
+ mService.addOnProjectionStateChangedListener(listener2,
fakeProjectionType | otherFakeProjectionType);
verify(listener, atLeastOnce()).asBinder(); // Called twice during register.
verify(listener2, atLeastOnce()).asBinder(); // Called twice during register.
@@ -795,11 +795,11 @@
}
@Test
- public void projectionStateChangeListener_unregisteredOnDeath() throws Exception {
- IOnProjectionStateChangeListener listener = mock(IOnProjectionStateChangeListener.class);
+ public void projectionStateChangedListener_unregisteredOnDeath() throws Exception {
+ IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
IBinder listenerBinder = mock(IBinder.class);
when(listener.asBinder()).thenReturn(listenerBinder);
- mService.addOnProjectionStateChangeListener(listener, PROJECTION_TYPE_ALL);
+ mService.addOnProjectionStateChangedListener(listener, PROJECTION_TYPE_ALL);
ArgumentCaptor<IBinder.DeathRecipient> listenerDeathRecipient = ArgumentCaptor.forClass(
IBinder.DeathRecipient.class);
verify(listenerBinder).linkToDeath(listenerDeathRecipient.capture(), anyInt());
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index a9e1a8f..bf49f3c 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -483,7 +483,16 @@
// check to see if these are recognized numbers, and use shortcuts if we can.
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
- if (tm.isEmergencyNumber(number)) {
+ boolean isEmergencyNumber = false;
+ try {
+ isEmergencyNumber = tm.isEmergencyNumber(number);
+ } catch (IllegalStateException ise) {
+ // Ignore the exception that Telephony is not up. Use PhoneNumberUtils API now.
+ // Ideally the PhoneNumberUtils API needs to be removed once the
+ // telphony service not up issue can be fixed (b/187412989)
+ isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(context, number);
+ }
+ if (isEmergencyNumber) {
cw.event = EVENT_EMERGENCY_NUMBER;
} else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) {
cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index d361db2..4d81b5e 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -263,7 +263,7 @@
return true;
}
return checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
- context, subId, callingPackage, callingFeatureId, message, true);
+ context, subId, callingPackage, callingFeatureId, message, true, true);
}
/**
@@ -286,14 +286,28 @@
*/
public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
String callingPackage, @Nullable String callingFeatureId, String message) {
+ return checkCallingOrSelfReadSubscriberIdentifiers(context, subId, callingPackage,
+ callingFeatureId, message, true);
+ }
+
+ /**
+ * Same as {@link #checkCallingOrSelfReadSubscriberIdentifiers(Context, int, String, String,
+ * String)} except this allows an additional parameter reportFailure. Caller may not want to
+ * report a failure when this is an internal/intermediate check, for example,
+ * SubscriptionController calls this with an INVALID_SUBID to check if caller has the required
+ * permissions to bypass carrier privilege checks.
+ * @param reportFailure Indicates if failure should be reported.
+ */
+ public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
+ String callingPackage, @Nullable String callingFeatureId, String message,
+ boolean reportFailure) {
if (checkCallingOrSelfUseIccAuthWithDeviceIdentifier(context, callingPackage,
callingFeatureId, message)) {
return true;
}
return checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
- context, subId, callingPackage, callingFeatureId, message, false);
+ context, subId, callingPackage, callingFeatureId, message, false, reportFailure);
}
-
/**
* Checks whether the app with the given pid/uid can read device identifiers.
*
@@ -314,7 +328,7 @@
*/
private static boolean checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
- String message, boolean allowCarrierPrivilegeOnAnySub) {
+ String message, boolean allowCarrierPrivilegeOnAnySub, boolean reportFailure) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
@@ -334,8 +348,12 @@
return true;
}
- return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
- message);
+ if (reportFailure) {
+ return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
+ message);
+ } else {
+ return false;
+ }
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4004e37..21bb43c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -572,6 +572,13 @@
/**
* @hide
*/
+ public void clearGroupUuid() {
+ this.mGroupUUID = null;
+ }
+
+ /**
+ * @hide
+ */
public List<String> getEhplmns() {
return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns);
}
diff --git a/tests/ActivityViewTest/Android.bp b/tests/ActivityViewTest/Android.bp
deleted file mode 100644
index 95178a0..0000000
--- a/tests/ActivityViewTest/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "ActivityViewTest",
- srcs: ["src/**/*.java"],
- platform_apis: true,
- certificate: "platform",
-}
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
deleted file mode 100644
index 7563a25..0000000
--- a/tests/ActivityViewTest/AndroidManifest.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.test.activityview">
- <uses-permission android:name="android.permission.INJECT_EVENTS"/>
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
- <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
- <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
-
- <uses-sdk android:targetSdkVersion="27"/>
- <application android:label="ActivityViewTest">
- <activity android:name=".ActivityViewMainActivity"
- android:label="AV Main"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".ActivityViewActivity"
- android:label="AV"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:windowSoftInputMode="stateHidden|adjustResize">
- </activity>
-
- <activity android:name=".ActivityViewResizeActivity"
- android:label="AV Resize"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:windowSoftInputMode="stateHidden|adjustResize">
- </activity>
-
- <activity android:name=".ActivityViewScrollActivity"
- android:label="AV Scroll"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
- android:windowSoftInputMode="stateHidden">
- </activity>
-
- <activity android:name=".ActivityViewTestActivity"
- android:resizeableActivity="true"
- android:theme="@*android:style/Theme.NoTitleBar"
- android:exported="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
- </activity>
-
- <activity android:name=".ActivityViewVisibilityActivity"
- android:label="AV Visibility"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
- </activity>
- </application>
-</manifest>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_activity.xml
deleted file mode 100644
index 67c01f8..0000000
--- a/tests/ActivityViewTest/res/layout/activity_view_activity.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:text="Launch test activity" />
-
- <Button
- android:id="@+id/activity_pick_launch_button"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:text="Launch from picker" />
-
- </LinearLayout>
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
deleted file mode 100644
index efcaef6..0000000
--- a/tests/ActivityViewTest/res/layout/activity_view_main_activity.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <Button
- android:id="@+id/activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test ActivityView"
- android:textAllCaps="false"/>
-
- <Button
- android:id="@+id/scroll_activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test Scroll ActivityView"
- android:textAllCaps="false"/>
-
- <Button
- android:id="@+id/resize_activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test Resize ActivityView"
- android:textAllCaps="false"/>
-
- <Button
- android:id="@+id/visibility_activity_view_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Test ActivityView Visibility"
- android:textAllCaps="false"/>
-</LinearLayout>
diff --git a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
deleted file mode 100644
index 18d86e3..0000000
--- a/tests/ActivityViewTest/res/layout/activity_view_resize_activity.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Launch" />
-
- <Button
- android:id="@+id/activity_resize_button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Resize" />
- </LinearLayout>
-
- <SeekBar
- android:id="@+id/activity_view_seek_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="600dp" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
deleted file mode 100644
index 879c2c20..0000000
--- a/tests/ActivityViewTest/res/layout/activity_view_scroll_activity.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:text="Launch" />
-
- <ScrollView
- android:id="@+id/activity_view_host_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:color="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <View
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:layout_gravity="center_horizontal"
- android:background="#eeeeee" />
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:background="#fce4ec" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="300dp"
- android:layout_gravity="center_horizontal"
- android:background="#eeeeee" />
- </LinearLayout>
- </ScrollView>
-</LinearLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
deleted file mode 100644
index 338d68a..0000000
--- a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_activity_root"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ffe0b2">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:orientation="vertical"
- android:background="#00000000" >
- <TextView
- android:id="@+id/test_activity_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
- <TextView
- android:id="@+id/test_activity_touch_state"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
- </LinearLayout>
-
- <TextView
- android:id="@+id/test_activity_width_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
-
- <TextView
- android:id="@+id/test_activity_height_text"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:textColor="@android:color/black"
- android:background="#00000000"
- android:gravity="center" />
-
- <EditText
- android:id="@+id/test_activity_edittext"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_margin="16dp" />
-</RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml
deleted file mode 100644
index d29d4df..0000000
--- a/tests/ActivityViewTest/res/layout/activity_view_visibility_activity.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#cfd8dc">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/activity_launch_button"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:text="Launch test activity" />
-
- <Spinner
- android:id="@+id/visibility_spinner"
- android:layout_width="200dp"
- android:layout_height="match_parent"/>
-
- </LinearLayout>
-
- <ActivityView
- android:id="@+id/activity_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</LinearLayout>
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
deleted file mode 100644
index f7c60fc..0000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewActivity.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright (c) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.test.activityview;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.widget.Button;
-
-public class ActivityViewActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_activity);
-
- final ActivityView activityView = findViewById(R.id.activity_view);
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(intent);
- });
- final Button pickActivityLaunchButton = findViewById(R.id.activity_pick_launch_button);
- pickActivityLaunchButton.setOnClickListener(v -> {
- final Intent intent = Intent.makeMainActivity(null);
- final Intent chooser = Intent.createChooser(intent,
- "Pick an app to launch in ActivityView");
- chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[] {
- new Intent(Intent.ACTION_MAIN)
- .addCategory("com.android.internal.category.PLATLOGO")
- });
- if (intent.resolveActivity(getPackageManager()) != null) {
- chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(chooser);
- }
- });
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
deleted file mode 100644
index 4f09c28..0000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewMainActivity.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright (c) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.test.activityview;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-public class ActivityViewMainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_main_activity);
-
- findViewById(R.id.activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewActivity.class)));
-
- findViewById(R.id.scroll_activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewScrollActivity.class)));
-
- findViewById(R.id.resize_activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewResizeActivity.class)));
-
- findViewById(R.id.visibility_activity_view_button).setOnClickListener(
- v -> startActivity(new Intent(this, ActivityViewVisibilityActivity.class)));
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
deleted file mode 100644
index 8860a77..0000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewResizeActivity.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Copyright (c) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.test.activityview;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-
-public class ActivityViewResizeActivity extends Activity {
- private static final int SMALL_SIZE = 600;
- private static final int LARGE_SIZE = 1200;
-
- private ActivityView mActivityView;
-
- private boolean mFlipSize;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_resize_activity);
-
- mActivityView = findViewById(R.id.activity_view);
-
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- mActivityView.startActivity(intent);
- });
- final Button resizeButton = findViewById(R.id.activity_resize_button);
- if (resizeButton != null) {
- resizeButton.setOnClickListener(v -> {
- LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
- params.height = mFlipSize ? SMALL_SIZE : LARGE_SIZE;
- mFlipSize = !mFlipSize;
- mActivityView.setLayoutParams(params);
- });
- }
- final SeekBar seekBar = findViewById(R.id.activity_view_seek_bar);
- if (seekBar != null) {
- seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- final LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) mActivityView.getLayoutParams();
- params.height = SMALL_SIZE + progress * 10;
- mActivityView.setLayoutParams(params);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
- }
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
deleted file mode 100644
index 5654366..0000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewScrollActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright (c) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.test.activityview;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-
-public class ActivityViewScrollActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_scroll_activity);
-
- final ActivityView activityView = findViewById(R.id.activity_view);
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(intent);
- });
- findViewById(R.id.activity_view_host_scroll_view).setOnScrollChangeListener(
- (View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
- -> activityView.onLocationChanged());
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
deleted file mode 100644
index 52aba2b..0000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/**
- * Copyright (c) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.test.activityview;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
-
-import android.app.Activity;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.widget.TextView;
-
-public class ActivityViewTestActivity extends Activity {
- private static final String TAG = "ActivityViewTestActivity";
-
- private View mRoot;
- private TextView mTextView;
- private TextView mWidthTextView;
- private TextView mHeightTextView;
- private TextView mTouchStateTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_test_activity);
- mRoot = findViewById(R.id.test_activity_root);
- mTextView = findViewById(R.id.test_activity_title);
- mWidthTextView = findViewById(R.id.test_activity_width_text);
- mHeightTextView = findViewById(R.id.test_activity_height_text);
- mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
- ViewTreeObserver viewTreeObserver = mRoot.getViewTreeObserver();
- if (viewTreeObserver.isAlive()) {
- viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
- }
- updateStateText("CREATED");
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- updateStateText("STARTED");
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- updateStateText("RESUMED");
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- updateStateText("PAUSED");
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- updateStateText("STOPPED");
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- updateDimensionTexts();
- }
-
- private void updateStateText(String state) {
- Log.d(TAG, state);
- mTextView.setText(state);
- }
-
- private void updateDimensionTexts() {
- mWidthTextView.setText("" + mRoot.getWidth());
- mHeightTextView.setText("" + mRoot.getHeight());
- }
-
- private void updateTouchState(MotionEvent event) {
- switch (event.getAction()) {
- case ACTION_DOWN:
- case ACTION_MOVE:
- mTouchStateTextView.setText("[" + event.getX() + "," + event.getY() + "]");
- break;
- case ACTION_UP:
- case ACTION_CANCEL:
- mTouchStateTextView.setText("");
- break;
- }
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- updateTouchState(event);
- return super.dispatchTouchEvent(event);
- }
-}
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java
deleted file mode 100644
index ecd2cf3..0000000
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewVisibilityActivity.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (c) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.test.activityview;
-
-import static android.view.View.GONE;
-import static android.view.View.INVISIBLE;
-import static android.view.View.VISIBLE;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.Spinner;
-
-public class ActivityViewVisibilityActivity extends Activity {
- private static final String[] sVisibilityOptions = {"VISIBLE", "INVISIBLE", "GONE"};
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_view_visibility_activity);
-
- final ActivityView activityView = findViewById(R.id.activity_view);
- final Button launchButton = findViewById(R.id.activity_launch_button);
- launchButton.setOnClickListener(v -> {
- final Intent intent = new Intent(this, ActivityViewTestActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- activityView.startActivity(intent);
- });
-
- final Spinner visibilitySpinner = findViewById(R.id.visibility_spinner);
- final ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
- android.R.layout.simple_spinner_item, sVisibilityOptions);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- visibilitySpinner.setAdapter(adapter);
- visibilitySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- switch (position) {
- case 0:
- activityView.setVisibility(VISIBLE);
- break;
- case 1:
- activityView.setVisibility(INVISIBLE);
- break;
- case 2:
- activityView.setVisibility(GONE);
- break;
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
- }
-}
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
index 7417455..35859fe 100644
--- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -48,6 +48,8 @@
public void tearDown() throws Exception {
removeSecondaryUserIfNecessary();
runPhaseForUsers("cleanUp", mOriginalUserId);
+ uninstallPackage("com.android.cts.install.lib.testapp.A");
+ uninstallPackage("com.android.cts.install.lib.testapp.B");
}
@Before
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 9793c34..898b8d4 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -112,7 +112,8 @@
@After
public void tearDown() throws Exception {
- expectCommandToSucceed("cmd font clear");
+ // Ignore errors because this may fail if updatable system font is not enabled.
+ runShellCommand("cmd font clear", null);
if (mKeyId != null) {
expectCommandToSucceed("mini-keyctl unlink " + mKeyId + " .fs-verity");
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index aa4b5f8..9ecd82f 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -77,6 +77,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -98,6 +99,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@@ -326,6 +328,17 @@
return subIdToGroupMap.get(invocation.getArgument(0));
}).when(snapshot).getGroupForSubId(anyInt());
+ doAnswer(invocation -> {
+ final ParcelUuid subGrp = invocation.getArgument(0);
+ final Set<Integer> subIds = new ArraySet<>();
+ for (Entry<Integer, ParcelUuid> entry : subIdToGroupMap.entrySet()) {
+ if (entry.getValue().equals(subGrp)) {
+ subIds.add(entry.getKey());
+ }
+ }
+ return subIds;
+ }).when(snapshot).getAllSubIdsInGroup(any());
+
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
@@ -914,6 +927,18 @@
verify(mMockPolicyListener).onPolicyChanged();
}
+ @Test
+ public void testVcnSubIdChangeUpdatesPolicyListener() throws Exception {
+ startAndGetVcnInstance(TEST_UUID_2);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(TEST_UUID_2),
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2));
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
private void triggerVcnSafeMode(
@NonNull ParcelUuid subGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,