Merge "Fix issue where location provider overlay may not bind" into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index aec464d..e96d07f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -55,6 +55,7 @@
import android.util.SparseArrayMap;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.SparseSetArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -158,19 +159,6 @@
@GuardedBy("mLock")
private final SparseLongArray mLastSeenConstraintTimesElapsed = new SparseLongArray();
- private DeviceIdleInternal mDeviceIdleInternal;
- private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>();
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
- mHandler.post(FlexibilityController.this::updatePowerAllowlistCache);
- break;
- }
- }
- };
@VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
@@ -182,6 +170,7 @@
private final FcHandler mHandler;
@VisibleForTesting
final PrefetchController mPrefetchController;
+ private final SpecialAppTracker mSpecialAppTracker;
/**
* Stores the beginning of prefetch jobs lifecycle per app as a maximum of
@@ -355,16 +344,16 @@
mPercentsToDropConstraints =
FcConfig.DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS;
mPrefetchController = prefetchController;
+ mSpecialAppTracker = new SpecialAppTracker();
if (mFlexibilityEnabled) {
- registerBroadcastReceiver();
+ mSpecialAppTracker.startTracking();
}
}
@Override
public void onSystemServicesReady() {
- mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
- mHandler.post(FlexibilityController.this::updatePowerAllowlistCache);
+ mSpecialAppTracker.onSystemServicesReady();
}
@Override
@@ -453,6 +442,7 @@
final int userId = UserHandle.getUserId(uid);
mPrefetchLifeCycleStart.delete(userId, packageName);
mJobScoreTrackers.delete(uid, packageName);
+ mSpecialAppTracker.onAppRemoved(userId, packageName);
for (int i = mJobsToCheck.size() - 1; i >= 0; --i) {
final JobStatus js = mJobsToCheck.valueAt(i);
if ((js.getSourceUid() == uid && js.getSourcePackageName().equals(packageName))
@@ -466,6 +456,7 @@
@GuardedBy("mLock")
public void onUserRemovedLocked(int userId) {
mPrefetchLifeCycleStart.delete(userId);
+ mSpecialAppTracker.onUserRemoved(userId);
for (int u = mJobScoreTrackers.numMaps() - 1; u >= 0; --u) {
final int uid = mJobScoreTrackers.keyAt(u);
if (UserHandle.getUserId(uid) == userId) {
@@ -496,9 +487,10 @@
// Only exclude DEFAULT+ priority jobs for BFGS+ apps
|| (mService.getUidBias(js.getSourceUid()) >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE
&& js.getEffectivePriority() >= PRIORITY_DEFAULT)
- // For apps in the power allowlist, automatically exclude DEFAULT+ priority jobs.
+ // For special/privileged apps, automatically exclude DEFAULT+ priority jobs.
|| (js.getEffectivePriority() >= PRIORITY_DEFAULT
- && mPowerAllowlistedApps.contains(js.getSourcePackageName()))
+ && mSpecialAppTracker.isSpecialApp(
+ js.getSourceUserId(), js.getSourcePackageName()))
|| hasEnoughSatisfiedConstraintsLocked(js)
|| mService.isCurrentlyRunningLocked(js);
}
@@ -827,39 +819,6 @@
mFcConfig.processConstantLocked(properties, key);
}
- private void registerBroadcastReceiver() {
- IntentFilter filter = new IntentFilter(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- private void unregisterBroadcastReceiver() {
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
-
- private void updatePowerAllowlistCache() {
- if (mDeviceIdleInternal == null) {
- return;
- }
-
- // Don't call out to DeviceIdleController with the lock held.
- final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle();
- final ArraySet<String> changedPkgs = new ArraySet<>();
- synchronized (mLock) {
- changedPkgs.addAll(mPowerAllowlistedApps);
- mPowerAllowlistedApps.clear();
- for (final String pkgName : allowlistedPkgs) {
- mPowerAllowlistedApps.add(pkgName);
- if (changedPkgs.contains(pkgName)) {
- changedPkgs.remove(pkgName);
- } else {
- changedPkgs.add(pkgName);
- }
- }
- mPackagesToCheck.addAll(changedPkgs);
- mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES);
- }
- }
-
@VisibleForTesting
class FlexibilityTracker {
final ArrayList<ArraySet<JobStatus>> mTrackedJobs;
@@ -1343,12 +1302,12 @@
mFlexibilityEnabled = true;
mPrefetchController
.registerPrefetchChangedListener(mPrefetchChangedListener);
- registerBroadcastReceiver();
+ mSpecialAppTracker.startTracking();
} else {
mFlexibilityEnabled = false;
mPrefetchController
.unRegisterPrefetchChangedListener(mPrefetchChangedListener);
- unregisterBroadcastReceiver();
+ mSpecialAppTracker.stopTracking();
}
}
break;
@@ -1653,6 +1612,176 @@
return mFcConfig;
}
+ private class SpecialAppTracker {
+ /**
+ * Lock for objects inside this class. This should never be held when attempting to acquire
+ * {@link #mLock}. It is fine to acquire this if already holding {@link #mLock}.
+ */
+ private final Object mSatLock = new Object();
+
+ private DeviceIdleInternal mDeviceIdleInternal;
+
+ /** Set of all apps that have been deemed special, keyed by user ID. */
+ private final SparseSetArray<String> mSpecialApps = new SparseSetArray<>();
+ @GuardedBy("mSatLock")
+ private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
+ mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
+ break;
+ }
+ }
+ };
+
+ public boolean isSpecialApp(final int userId, @NonNull String packageName) {
+ synchronized (mSatLock) {
+ if (mSpecialApps.contains(UserHandle.USER_ALL, packageName)) {
+ return true;
+ }
+ if (mSpecialApps.contains(userId, packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSpecialAppInternal(final int userId, @NonNull String packageName) {
+ synchronized (mSatLock) {
+ if (mPowerAllowlistedApps.contains(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void onAppRemoved(final int userId, String packageName) {
+ synchronized (mSatLock) {
+ // Don't touch the USER_ALL set here. If the app is completely removed from the
+ // device, any list that affects USER_ALL should update and this would eventually
+ // be updated with those lists no longer containing the app.
+ mSpecialApps.remove(userId, packageName);
+ }
+ }
+
+ private void onSystemServicesReady() {
+ mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
+
+ synchronized (mLock) {
+ if (mFlexibilityEnabled) {
+ mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
+ }
+ }
+ }
+
+ private void onUserRemoved(final int userId) {
+ synchronized (mSatLock) {
+ mSpecialApps.remove(userId);
+ }
+ }
+
+ private void startTracking() {
+ IntentFilter filter = new IntentFilter(
+ PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ updatePowerAllowlistCache();
+ }
+
+ private void stopTracking() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+
+ synchronized (mSatLock) {
+ mPowerAllowlistedApps.clear();
+ mSpecialApps.clear();
+ }
+ }
+
+ /**
+ * Update the processed special app set for the specified user ID, only looking at the
+ * specified set of apps. This method must <b>NEVER</b> be called while holding
+ * {@link #mSatLock}.
+ */
+ private void updateSpecialAppSetUnlocked(final int userId, @NonNull ArraySet<String> pkgs) {
+ // This method may need to acquire mLock, so ensure that mSatLock isn't held to avoid
+ // lock inversion.
+ if (Thread.holdsLock(mSatLock)) {
+ throw new IllegalStateException("Must never hold local mSatLock");
+ }
+ if (pkgs.size() == 0) {
+ return;
+ }
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+
+ synchronized (mSatLock) {
+ for (int i = pkgs.size() - 1; i >= 0; --i) {
+ final String pkgName = pkgs.valueAt(i);
+ if (isSpecialAppInternal(userId, pkgName)) {
+ if (mSpecialApps.add(userId, pkgName)) {
+ changedPkgs.add(pkgName);
+ }
+ } else if (mSpecialApps.remove(userId, pkgName)) {
+ changedPkgs.add(pkgName);
+ }
+ }
+ }
+
+ if (changedPkgs.size() > 0) {
+ synchronized (mLock) {
+ mPackagesToCheck.addAll(changedPkgs);
+ mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES);
+ }
+ }
+ }
+
+ private void updatePowerAllowlistCache() {
+ if (mDeviceIdleInternal == null) {
+ return;
+ }
+
+ // Don't call out to DeviceIdleController with the lock held.
+ final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle();
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+ synchronized (mSatLock) {
+ changedPkgs.addAll(mPowerAllowlistedApps);
+ mPowerAllowlistedApps.clear();
+ for (String pkgName : allowlistedPkgs) {
+ mPowerAllowlistedApps.add(pkgName);
+ if (!changedPkgs.remove(pkgName)) {
+ // The package wasn't in the previous set of allowlisted apps. Add it
+ // since its state has changed.
+ changedPkgs.add(pkgName);
+ }
+ }
+ }
+
+ // The full allowlist is currently user-agnostic, so use USER_ALL for these packages.
+ updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
+ }
+
+ public void dump(@NonNull IndentingPrintWriter pw) {
+ pw.println("Special apps:");
+ pw.increaseIndent();
+
+ synchronized (mSatLock) {
+ for (int u = 0; u < mSpecialApps.size(); ++u) {
+ pw.print(mSpecialApps.keyAt(u));
+ pw.print(": ");
+ pw.println(mSpecialApps.valuesAt(u));
+ }
+
+ pw.println();
+ pw.print("Power allowlisted packages: ");
+ pw.println(mPowerAllowlistedApps);
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+
@Override
@GuardedBy("mLock")
public void dumpConstants(IndentingPrintWriter pw) {
@@ -1690,8 +1819,7 @@
pw.decreaseIndent();
pw.println();
- pw.print("Power allowlisted packages: ");
- pw.println(mPowerAllowlistedApps);
+ mSpecialAppTracker.dump(pw);
pw.println();
mFlexibilityTracker.dump(pw, predicate, nowElapsed);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 062bdaa..d9b5d56 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10421,8 +10421,7 @@
@FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String);
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilterToAutoTransact(@NonNull String);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String, boolean);
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean defaultToObserveMode();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 73914a2..4ef0a1b 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -67,6 +67,8 @@
mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT,
android.content.IntentSender.class);
+ String targetPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ final UserManager userManager = UserManager.get(this);
if (mUserId == UserHandle.USER_NULL) {
Log.wtf(TAG, "Invalid user id: " + mUserId + ". Stopping.");
@@ -74,13 +76,20 @@
return;
}
+ if (android.os.Flags.allowPrivateProfile()
+ && !userManager.isManagedProfile(mUserId)) {
+ Log.e(TAG, "Unlaunchable activity for target package " + targetPackageName
+ + " called for a non-managed-profile " + mUserId);
+ finish();
+ return;
+ }
+
if (mReason != UNLAUNCHABLE_REASON_QUIET_MODE) {
Log.wtf(TAG, "Invalid unlaunchable type: " + mReason);
finish();
return;
}
- String targetPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
boolean showEmergencyCallButton =
(targetPackageName != null && targetPackageName.equals(
mTelecomManager.getDefaultDialerPackage(UserHandle.of(mUserId))));
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 7b53ca6..0fb7c95 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -204,7 +204,7 @@
method public boolean isDefaultServiceForAid(android.content.ComponentName, String);
method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
method public boolean removeAidsForService(android.content.ComponentName, String);
method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 65d0625..64f7fa4 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -32,7 +32,7 @@
boolean setDefaultForNextTap(int userHandle, in ComponentName service);
boolean setDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
- boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter);
+ boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter, boolean autoTransact);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
boolean unsetOffHostForService(int userHandle, in ComponentName service);
AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index c81b95b..e62e37b 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -681,34 +681,18 @@
/**
* Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
- * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
- * {@link ApduServiceInfo#addPollingLoopFilterToAutoTransact(String)} multiple times will
- * cause the value to be overwritten each time.
+ * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
+ * multiple times will cause the value to be overwritten each time.
* @param pollingLoopFilter the polling loop filter to add, must be a valide hexadecimal string
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void addPollingLoopFilter(@NonNull String pollingLoopFilter) {
- mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), false);
+ public void addPollingLoopFilter(@NonNull String pollingLoopFilter,
+ boolean autoTransact) {
+ mAutoTransact.put(pollingLoopFilter, autoTransact);
}
/**
- * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will cause the
- * device to exit observe mode, just as if
- * {@link android.nfc.NfcAdapter#setObserveModeEnabled(boolean)} had been called with true,
- * allowing transactions to proceed. The matching frame will also be delivered to
- * {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
- * {@link ApduServiceInfo#addPollingLoopFilter(String)} multiple times will
- * cause the value to be overwritten each time.
- *
- * @param pollingLoopFilter the polling loop filter to add, must be a valide hexadecimal string
- */
- @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void addPollingLoopFilterToAutoTransact(@NonNull String pollingLoopFilter) {
- mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), true);
- }
-
- /**
* Remove a Polling Loop Filter. Custom NFC polling frames that match this filter will no
* longer be delivered to {@link HostApduService#processPollingFrames(List)}.
* @param pollingLoopFilter this polling loop filter to add.
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index e681a85..47ddd9d 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -42,6 +42,7 @@
import android.util.Log;
import java.util.HashMap;
+import java.util.HexFormat;
import java.util.List;
import java.util.regex.Pattern;
@@ -59,7 +60,6 @@
*/
public final class CardEmulation {
private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
- private static final Pattern PLF_PATTERN = Pattern.compile("[0-9A-Fa-f]{1,32}");
static final String TAG = "CardEmulation";
@@ -360,21 +360,28 @@
}
/**
- * Register a polling loop filter (PLF) for a HostApduService. The PLF can be sequence of an
- * even number of hexadecimal numbers (0-9, A-F or a-f). When non-standard polling loop frame
- * matches this sequence exactly, it may be delivered to
- * {@link HostApduService#processPollingFrames(List)} if this service is currently
- * preferred or there are no other services registered for this filter.
+ * Register a polling loop filter (PLF) for a HostApduService and indicate whether it should
+ * auto-transact or not. The PLF can be sequence of an
+ * even number of at least 2 hexadecimal numbers (0-9, A-F or a-f), representing a series of
+ * bytes. When non-standard polling loop frame matches this sequence exactly, it may be
+ * delivered to {@link HostApduService#processPollingFrames(List)}. If auto-transact is set to
+ * true, then observe mode will also be disabled. if this service is currently preferred or
+ * there are no other services registered for this filter.
* @param service The HostApduService to register the filter for
* @param pollingLoopFilter The filter to register
+ * @param autoTransact true to have the NFC stack automatically disable observe mode and allow
+ * transactions to proceed when this filter matches, false otherwise
* @return true if the filter was registered, false otherwise
+ * @throws IllegalArgumentException if the passed in string doesn't parse to at least one byte
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public boolean registerPollingLoopFilterForService(@NonNull ComponentName service,
- @NonNull String pollingLoopFilter) {
+ @NonNull String pollingLoopFilter, boolean autoTransact) {
+ pollingLoopFilter = validatePollingLoopFilter(pollingLoopFilter);
+
try {
return sService.registerPollingLoopFilterForService(mContext.getUser().getIdentifier(),
- service, pollingLoopFilter);
+ service, pollingLoopFilter, autoTransact);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -384,7 +391,8 @@
}
try {
return sService.registerPollingLoopFilterForService(
- mContext.getUser().getIdentifier(), service, pollingLoopFilter);
+ mContext.getUser().getIdentifier(), service,
+ pollingLoopFilter, autoTransact);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
@@ -979,15 +987,14 @@
* @hide
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static boolean isValidPollingLoopFilter(@NonNull String pollingLoopFilter) {
+ public static @NonNull String validatePollingLoopFilter(@NonNull String pollingLoopFilter) {
// Verify hex characters
- if (!PLF_PATTERN.matcher(pollingLoopFilter).matches()) {
- Log.e(TAG, "Polling Loop Filter " + pollingLoopFilter
- + " is not a valid Polling Loop Filter.");
- return false;
+ byte[] plfBytes = HexFormat.of().parseHex(pollingLoopFilter);
+ if (plfBytes.length == 0) {
+ throw new IllegalArgumentException(
+ "Polling loop filter must contain at least one byte.");
}
-
- return true;
+ return HexFormat.of().withUpperCase().formatHex(plfBytes);
}
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 904e184..cf6aab6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -403,7 +403,7 @@
resolvedPath = info.getResolvedBaseApkPath();
}
if (info == null || !info.isSealed() || resolvedPath == null) {
- Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ Log.w(TAG, "Session " + sessionId + " in funky state; ignoring");
finish();
return;
}
@@ -418,7 +418,7 @@
-1 /* defaultValue */);
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.isPreApprovalRequested()) {
- Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ Log.w(TAG, "Session " + sessionId + " in funky state; ignoring");
finish();
return;
}
@@ -839,7 +839,9 @@
// work for the multiple user case, i.e. the caller task user and started
// Activity user are not the same. To avoid having multiple PIAs in the task,
// finish the current PackageInstallerActivity
- finish();
+ // Because finish() is overridden to set the installation result, we must use
+ // the original finish() method, or the confirmation dialog fails to appear.
+ PackageInstallerActivity.super.finish();
}
}, 500);
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 8b2a0ec..05e07a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -377,7 +377,9 @@
if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
else SysuiR.string::dnd_is_on.name
).also { data ->
- clock?.run { events.onZenDataChanged(data) }
+ mainExecutor.execute {
+ clock?.run { events.onZenDataChanged(data) }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index b0cc3bd..cd5b124 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -438,9 +438,20 @@
// Play haptics
launch {
- viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
- if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
- vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
+ viewModel.hapticsToPlay.collect { haptics ->
+ if (haptics.hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
+ if (haptics.flag != null) {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.hapticFeedbackConstant,
+ haptics.flag,
+ )
+ } else {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.hapticFeedbackConstant,
+ )
+ }
viewModel.clearHaptics()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 788991d..c933e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -53,6 +53,7 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
/** ViewModel for BiometricPrompt. */
@@ -144,9 +145,10 @@
private val _forceLargeSize = MutableStateFlow(false)
private val _forceMediumSize = MutableStateFlow(false)
- private val _hapticsToPlay = MutableStateFlow(HapticFeedbackConstants.NO_HAPTICS)
+ private val _hapticsToPlay =
+ MutableStateFlow(HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, /* flag= */ null))
- /** Event fired to the view indicating a [HapticFeedbackConstants] to be played */
+ /** Event fired to the view indicating a [HapticsToPlay] */
val hapticsToPlay = _hapticsToPlay.asStateFlow()
/** The current position of the prompt */
@@ -686,16 +688,26 @@
}
private fun vibrateOnSuccess() {
- _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
+ _hapticsToPlay.value =
+ HapticsToPlay(
+ HapticFeedbackConstants.CONFIRM,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ )
}
private fun vibrateOnError() {
- _hapticsToPlay.value = HapticFeedbackConstants.REJECT
+ _hapticsToPlay.value =
+ HapticsToPlay(
+ HapticFeedbackConstants.REJECT,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ )
}
- /** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */
+ /** Clears the [hapticsToPlay] variable by setting its constant to the NO_HAPTICS default. */
fun clearHaptics() {
- _hapticsToPlay.value = HapticFeedbackConstants.NO_HAPTICS
+ _hapticsToPlay.update { previous ->
+ HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, previous.flag)
+ }
}
companion object {
@@ -724,3 +736,9 @@
val isStarted: Boolean
get() = this == Normal || this == Delayed
}
+
+/**
+ * The state of haptic feedback to play. It is composed by a [HapticFeedbackConstants] and a
+ * [HapticFeedbackConstants] flag.
+ */
+data class HapticsToPlay(val hapticFeedbackConstant: Int, val flag: Int?)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
index 5a71bd6..7dfd53e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
@@ -18,7 +18,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-private const val FLEXI_NOTIFS = false
+private const val FLEXI_NOTIFS = true
/**
* Returns whether flexiglass is displaying notifications, which is currently an optional piece of
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 8431fbc..9dedf5c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -18,9 +18,6 @@
import android.content.Context
import android.media.AudioManager
-import com.android.settingslib.media.data.repository.SpatializerRepository
-import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
-import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
@@ -66,16 +63,5 @@
notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
): AudioVolumeInteractor =
AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
-
- @Provides
- fun provdieSpatializerRepository(
- audioManager: AudioManager,
- @Background backgroundContext: CoroutineContext,
- ): SpatializerRepository =
- SpatializerRepositoryImpl(audioManager.spatializer, backgroundContext)
-
- @Provides
- fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
- SpatializerInteractor(repository)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
new file mode 100644
index 0000000..18a9161
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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.volume.dagger
+
+import android.media.AudioManager
+import android.media.Spatializer
+import com.android.settingslib.media.data.repository.SpatializerRepository
+import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
+import com.android.settingslib.media.domain.interactor.SpatializerInteractor
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.Module
+import dagger.Provides
+import kotlin.coroutines.CoroutineContext
+
+/** Spatializer module. */
+@Module
+interface SpatializerModule {
+ companion object {
+ @Provides
+ fun provideSpatializer(
+ audioManager: AudioManager,
+ ): Spatializer = audioManager.spatializer
+
+ @Provides
+ fun provdieSpatializerRepository(
+ spatializer: Spatializer,
+ @Background backgroundContext: CoroutineContext,
+ ): SpatializerRepository = SpatializerRepositoryImpl(spatializer, backgroundContext)
+
+ @Provides
+ fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
+ SpatializerInteractor(repository)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index c6aee42..64a5644 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -59,7 +59,8 @@
AudioModule.class,
AncModule.class,
CaptioningModule.class,
- MediaDevicesModule.class
+ MediaDevicesModule.class,
+ SpatializerModule.class,
},
subcomponents = {
VolumePanelComponent.class
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index ff68fe3..140849b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -241,19 +241,27 @@
viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- val confirmConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmConstant)
+ val confirmHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(confirmHaptics?.hapticFeedbackConstant)
.isEqualTo(
if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
else HapticFeedbackConstants.CONFIRM
)
+ assertThat(confirmHaptics?.flag)
+ .isEqualTo(
+ if (expectConfirmation) null
+ else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
+ )
if (expectConfirmation) {
viewModel.confirmAuthenticated()
}
- val confirmedConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmedConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+ val confirmedHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(confirmedHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(confirmedHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
@@ -265,16 +273,21 @@
viewModel.confirmAuthenticated()
}
- val currentConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+ val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
viewModel.showTemporaryError("test", "messageAfterError", false)
- val currentConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
@@ -800,8 +813,9 @@
hapticFeedback = true,
)
- val constant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(constant).isEqualTo(HapticFeedbackConstants.REJECT)
+ val haptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
@@ -813,8 +827,8 @@
hapticFeedback = false,
)
- val constant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(constant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
+ val haptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
}
private suspend fun TestScope.showTemporaryErrors(