diff options
76 files changed, 1699 insertions, 447 deletions
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java index 434b8e56dd36..bd9111228eac 100644 --- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java +++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java @@ -343,13 +343,14 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.start( + final RecordingCanvas c = node.startRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); state.resumeTiming(); textView.onDraw(c); + node.endRecording(); } } @@ -369,13 +370,14 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.start( + final RecordingCanvas c = node.startRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); state.resumeTiming(); textView.onDraw(c); + node.endRecording(); } } @@ -397,13 +399,14 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.start( + final RecordingCanvas c = node.startRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); state.resumeTiming(); textView.onDraw(c); + node.endRecording(); } } @@ -426,13 +429,14 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.start( + final RecordingCanvas c = node.startRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); state.resumeTiming(); textView.onDraw(c); + node.endRecording(); } } } diff --git a/api/current.txt b/api/current.txt index a5724fece528..fd09e6b00439 100755 --- a/api/current.txt +++ b/api/current.txt @@ -6184,6 +6184,7 @@ package android.app { public final class UiAutomation { method public void adoptShellPermissionIdentity(); + method public void adoptShellPermissionIdentity(java.lang.String...); method public void clearWindowAnimationFrameStats(); method public boolean clearWindowContentFrameStats(int); method public void dropShellPermissionIdentity(); diff --git a/api/system-current.txt b/api/system-current.txt index 237d4c427e90..4c975db6e971 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1088,6 +1088,7 @@ package android.content { field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; field public static final java.lang.String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP"; field public static final java.lang.String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE"; + field public static final java.lang.String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; } public class IntentFilter implements android.os.Parcelable { diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index f0f599317352..eb498f596141 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -71,6 +71,9 @@ const int FIELD_ID_STRINGS = 9; #define STATS_DATA_DIR "/data/misc/stats-data" +// Cool down period for writing data to disk to avoid overwriting files. +#define WRITE_DATA_COOL_DOWN_SEC 5 + StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager, const sp<AlarmMonitor>& anomalyAlarmMonitor, @@ -508,6 +511,16 @@ void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, void StatsLogProcessor::WriteDataToDiskLocked(const DumpReportReason dumpReportReason) { const int64_t timeNs = getElapsedRealtimeNs(); + // Do not write to disk if we already have in the last few seconds. + // This is to avoid overwriting files that would have the same name if we + // write twice in the same second. + if (static_cast<unsigned long long> (timeNs) < + mLastWriteTimeNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { + ALOGI("Statsd skipping writing data to disk. Already wrote data in last %d seconds", + WRITE_DATA_COOL_DOWN_SEC); + return; + } + mLastWriteTimeNs = timeNs; for (auto& pair : mMetricsManagers) { WriteDataToDiskLocked(pair.first, timeNs, dumpReportReason); } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index ecfd819a3399..a5ce9b65f899 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -177,6 +177,9 @@ private: long mLastPullerCacheClearTimeSec = 0; + // Last time we wrote data to disk. + int64_t mLastWriteTimeNs = 0; + #ifdef VERY_VERBOSE_PRINTING bool mPrintAllLogs = false; #endif diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 0d9a3938738c..099756691c4e 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -441,42 +441,6 @@ Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService; Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager; -Landroid/icu/impl/CurrencyData;-><init>()V -Landroid/icu/impl/ICUResourceBundle;->getULocale()Landroid/icu/util/ULocale; -Landroid/icu/impl/ICUResourceBundle;->getWithFallback(Ljava/lang/String;)Landroid/icu/impl/ICUResourceBundle; -Landroid/icu/impl/IllegalIcuArgumentException;-><init>(Ljava/lang/String;)V -Landroid/icu/text/ArabicShaping;-><init>(I)V -Landroid/icu/text/ArabicShaping;->isAlefMaksouraChar(C)Z -Landroid/icu/text/ArabicShaping;->isSeenTailFamilyChar(C)I -Landroid/icu/text/ArabicShaping;->isTailChar(C)Z -Landroid/icu/text/ArabicShaping;->isYehHamzaChar(C)Z -Landroid/icu/text/ArabicShaping;->shape(Ljava/lang/String;)Ljava/lang/String; -Landroid/icu/text/DateFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; -Landroid/icu/text/DateIntervalFormat;-><init>()V -Landroid/icu/text/DateTimePatternGenerator$DistanceInfo;-><init>()V -Landroid/icu/text/DecimalFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; -Landroid/icu/text/RuleBasedCollator;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; -Landroid/icu/text/SpoofChecker$ScriptSet;-><init>()V -Landroid/icu/text/SpoofChecker$ScriptSet;->and(I)V -Landroid/icu/text/SpoofChecker$ScriptSet;->isFull()Z -Landroid/icu/text/SpoofChecker$ScriptSet;->setAll()V -Landroid/icu/text/TimeZoneNames$DefaultTimeZoneNames$FactoryImpl;-><init>()V -Landroid/icu/text/Transliterator;->createFromRules(Ljava/lang/String;Ljava/lang/String;I)Landroid/icu/text/Transliterator; -Landroid/icu/text/Transliterator;->getInstance(Ljava/lang/String;)Landroid/icu/text/Transliterator; -Landroid/icu/text/Transliterator;->getInstance(Ljava/lang/String;I)Landroid/icu/text/Transliterator; -Landroid/icu/text/Transliterator;->transliterate(Landroid/icu/text/Replaceable;Landroid/icu/text/Transliterator$Position;Ljava/lang/String;)V -Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String; -Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; -Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; -Landroid/icu/util/PersianCalendar;-><init>(Ljava/util/Locale;)V -Landroid/icu/util/UResourceBundle;->getBundleInstance(Ljava/lang/String;Landroid/icu/util/ULocale;)Landroid/icu/util/UResourceBundle; -Landroid/icu/util/UResourceBundle;->getKey()Ljava/lang/String; -Landroid/icu/util/UResourceBundle;->getString()Ljava/lang/String; -Landroid/icu/util/UResourceBundle;->getType()I -Landroid/icu/util/UResourceBundleIterator;->hasNext()Z -Landroid/icu/util/UResourceBundleIterator;->next()Landroid/icu/util/UResourceBundle; -Landroid/inputmethodservice/IInputMethodSessionWrapper;->mCaller:Lcom/android/internal/os/HandlerCaller; -Landroid/inputmethodservice/IInputMethodWrapper;->mCaller:Lcom/android/internal/os/HandlerCaller; Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ICountryDetector; Landroid/location/ICountryListener$Stub;-><init>()V Landroid/location/IGeocodeProvider$Stub;-><init>()V @@ -1361,15 +1325,11 @@ Landroid/security/IKeystoreService;->exist(Ljava/lang/String;I)I Landroid/security/IKeystoreService;->generateKey(Ljava/lang/String;Landroid/security/keymaster/KeymasterArguments;[BIILandroid/security/keymaster/KeyCharacteristics;)I Landroid/security/IKeystoreService;->get(Ljava/lang/String;I)[B Landroid/security/IKeystoreService;->getState(I)I -Landroid/security/IKeystoreService;->get_pubkey(Ljava/lang/String;)[B -Landroid/security/IKeystoreService;->import_key(Ljava/lang/String;[BII)I Landroid/security/IKeystoreService;->insert(Ljava/lang/String;[BII)I Landroid/security/IKeystoreService;->is_hardware_backed(Ljava/lang/String;)I Landroid/security/IKeystoreService;->list(Ljava/lang/String;I)[Ljava/lang/String; Landroid/security/IKeystoreService;->reset()I -Landroid/security/IKeystoreService;->sign(Ljava/lang/String;[B)[B Landroid/security/IKeystoreService;->ungrant(Ljava/lang/String;I)I -Landroid/security/IKeystoreService;->verify(Ljava/lang/String;[B[B)I Landroid/security/keymaster/KeymasterBlobArgument;-><init>(ILandroid/os/Parcel;)V Landroid/security/keymaster/KeymasterBlobArgument;-><init>(I[B)V Landroid/security/keymaster/KeymasterBlobArgument;->blob:[B diff --git a/config/preloaded-classes b/config/preloaded-classes index 22fc5e80787f..14597ee919bf 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -4117,6 +4117,8 @@ com.android.internal.util.StateMachine$SmHandler$StateInfo com.android.internal.util.VirtualRefBasePtr com.android.internal.util.XmlUtils com.android.internal.util.XmlUtils$WriteMapCallback +com.android.internal.util.function.HeptConsumer +com.android.internal.util.function.HeptFunction com.android.internal.util.function.HexConsumer com.android.internal.util.function.HexFunction com.android.internal.util.function.QuadConsumer diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 069effd3ef19..d4151322e219 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -277,7 +277,12 @@ public abstract class ActivityManagerInternal { public abstract void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead, String hostingType, ComponentName hostingName); - /** Starts up the starting activity process for debugging if needed. */ + /** Starts up the starting activity process for debugging if needed. + * This function needs to be called synchronously from WindowManager context so the caller + * passes a lock {@code wmLock} and waits to be notified. + * + * @param wmLock calls {@code notify} on the object to wake up the caller. + */ public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags, - ProfilerInfo profilerInfo); + ProfilerInfo profilerInfo, Object wmLock); } diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java index 0643414727cf..229bee55e911 100644 --- a/core/java/android/app/DexLoadReporter.java +++ b/core/java/android/app/DexLoadReporter.java @@ -87,7 +87,7 @@ import java.util.Set; } @Override - public void report(List<BaseDexClassLoader> classLoadersChain, List<String> classPaths) { + public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) { if (classLoadersChain.size() != classPaths.size()) { Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch"); return; @@ -113,12 +113,12 @@ import java.util.Set; registerSecondaryDexForProfiling(dexPathsForRegistration); } - private void notifyPackageManager(List<BaseDexClassLoader> classLoadersChain, + private void notifyPackageManager(List<ClassLoader> classLoadersChain, List<String> classPaths) { // Get the class loader names for the binder call. List<String> classLoadersNames = new ArrayList<>(classPaths.size()); - for (BaseDexClassLoader bdc : classLoadersChain) { - classLoadersNames.add(bdc.getClass().getName()); + for (ClassLoader classLoader : classLoadersChain) { + classLoadersNames.add(classLoader.getClass().getName()); } String packageName = ActivityThread.currentPackageName(); try { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index e7597620e138..e2312a539ae4 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -475,7 +475,7 @@ interface IActivityManager { * instrumentation at a time. An active instrumentation is one running and * started from the shell. */ - void startDelegateShellPermissionIdentity(int uid); + void startDelegateShellPermissionIdentity(int uid, in String[] permissions); /** * Method for the shell UID to stop deletating its permission identity to an diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index ac4bf7d9c2c5..96da72a1b517 100644 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -47,7 +47,7 @@ interface IUiAutomationConnection { in ParcelFileDescriptor source); void grantRuntimePermission(String packageName, String permission, int userId); void revokeRuntimePermission(String packageName, String permission, int userId); - void adoptShellPermissionIdentity(int uid); + void adoptShellPermissionIdentity(int uid, in String[] permissions); void dropShellPermissionIdentity(); // Called from the system process. oneway void shutdown(); diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 5a25f5aed161..3f9627ed807c 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -354,12 +354,17 @@ public final class UiAutomation { } /** - * Adopt the permission identity of the shell UID. This allows you to call APIs protected - * permissions which normal apps cannot hold but are granted to the shell UID. If you - * already adopted the shell permission identity this method would be a no-op. - * Note that your permission state becomes that of the shell UID and it is not a - * combination of your and the shell UID permissions. + * Adopt the permission identity of the shell UID for all permissions. This allows + * you to call APIs protected permissions which normal apps cannot hold but are + * granted to the shell UID. If you already adopted all shell permissions by calling + * this method or {@link #adoptShellPermissionIdentity(String...)} a subsequent call + * would be a no-op. Note that your permission state becomes that of the shell UID + * and it is not a combination of your and the shell UID permissions. + * <p> + * <strong>Note:<strong/> Calling this method adopts all shell permissions and overrides + * any subset of adopted permissions via {@link #adoptShellPermissionIdentity(String...)}. * + * @see #adoptShellPermissionIdentity(String...) * @see #dropShellPermissionIdentity() */ public void adoptShellPermissionIdentity() { @@ -368,7 +373,33 @@ public final class UiAutomation { } try { // Calling out without a lock held. - mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid()); + mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), null); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re); + } + } + + /** + * Adopt the permission identity of the shell UID only for the provided permissions. + * This allows you to call APIs protected permissions which normal apps cannot hold + * but are granted to the shell UID. If you already adopted the specified shell + * permissions by calling this method or {@link #adoptShellPermissionIdentity()} a + * subsequent call would be a no-op. Note that your permission state becomes that of the + * shell UID and it is not a combination of your and the shell UID permissions. + * <p> + * <strong>Note:<strong/> Calling this method adopts only the specified shell permissions + * and overrides all adopted permissions via {@link #adoptShellPermissionIdentity()}. + * + * @see #adoptShellPermissionIdentity() + * @see #dropShellPermissionIdentity() + */ + public void adoptShellPermissionIdentity(String... permissions) { + synchronized (mLock) { + throwIfNotConnectedLocked(); + } + try { + // Calling out without a lock held. + mUiAutomationConnection.adoptShellPermissionIdentity(Process.myUid(), permissions); } catch (RemoteException re) { Log.e(LOG_TAG, "Error executing adopting shell permission identity!", re); } diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index b406d9e30a53..dc2f9838785c 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -18,7 +18,7 @@ package android.app; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; -import android.annotation.UnsupportedAppUsage; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.IPackageManager; import android.graphics.Bitmap; @@ -279,7 +279,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override - public void adoptShellPermissionIdentity(int uid) throws RemoteException { + public void adoptShellPermissionIdentity(int uid, @Nullable String[] permissions) + throws RemoteException { synchronized (mLock) { throwIfCalledByNotTrustedUidLocked(); throwIfShutdownLocked(); @@ -287,7 +288,7 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } final long identity = Binder.clearCallingIdentity(); try { - mActivityManager.startDelegateShellPermissionIdentity(uid); + mActivityManager.startDelegateShellPermissionIdentity(uid, permissions); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index aa34da8e0159..02f38a790570 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1727,10 +1727,14 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.UNINSTALL_ALL_USERS"; /** - * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity - * describing the last run version of the platform that was setup. + * A string that associates with a metadata entry, indicating the last run version of the + * platform that was setup. + * + * @see #ACTION_UPGRADE_SETUP + * * @hide */ + @SystemApi public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION"; /** diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 4a4de5160e80..a87ee572b6d0 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -475,7 +475,7 @@ interface IPackageManager { * @param classPaths the class paths corresponding to the class loaders names from * {@param classLoadersNames}. The the first element corresponds to the first class loader * and so on. A classpath is represented as a list of dex files separated by - * {@code File.pathSeparator}. + * {@code File.pathSeparator}, or null if the class loader's classpath is not known. * The dex files found in the first class path will be recorded in the usage file. * @param loaderIsa the ISA of the loader process */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c1ac061e1f79..d3e40452d9fb 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5825,16 +5825,16 @@ public abstract class PackageManager { * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state. * * <p>While in this state, the application's notifications will be hidden, any of its started - * activities will be stopped and it will not be able to show toasts or dialogs or ring the - * device. When the user tries to launch a suspended app, the system will, instead, show a + * activities will be stopped and it will not be able to show toasts or dialogs or play audio. + * When the user tries to launch a suspended app, the system will, instead, show a * dialog to the user informing them that they cannot use this app while it is suspended. * * <p>When an app is put into this state, the broadcast action * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast * receivers that included this action in their intent-filters, <em>including manifest * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED} - * is delivered when a previously suspended app is taken out of this state. - * </p> + * is delivered when a previously suspended app is taken out of this state. Apps are expected to + * use these to gracefully deal with transitions to and from this state. * * @return {@code true} if the calling package has been suspended, {@code false} otherwise. * diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 56e6aea815ec..64eae0cf4635 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1462,6 +1462,7 @@ public abstract class NotificationListenerService extends Service { private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL; private boolean mHidden; private boolean mAudiblyAlerted; + private boolean mNoisy; private ArrayList<Notification.Action> mSmartActions; private ArrayList<CharSequence> mSmartReplies; @@ -1636,6 +1637,11 @@ public abstract class NotificationListenerService extends Service { return mAudiblyAlerted; } + /** @hide */ + public boolean isNoisy() { + return mNoisy; + } + /** * @hide */ @@ -1646,7 +1652,8 @@ public abstract class NotificationListenerService extends Service { NotificationChannel channel, ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge, int userSentiment, boolean hidden, boolean audiblyAlerted, - ArrayList<Notification.Action> smartActions, ArrayList<CharSequence> smartReplies) { + boolean noisy, ArrayList<Notification.Action> smartActions, + ArrayList<CharSequence> smartReplies) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1663,6 +1670,7 @@ public abstract class NotificationListenerService extends Service { mUserSentiment = userSentiment; mHidden = hidden; mAudiblyAlerted = audiblyAlerted; + mNoisy = noisy; mSmartActions = smartActions; mSmartReplies = smartReplies; } @@ -1715,6 +1723,7 @@ public abstract class NotificationListenerService extends Service { private ArrayMap<String, Integer> mUserSentiment; private ArrayMap<String, Boolean> mHidden; private ArrayMap<String, Boolean> mAudiblyAlerted; + private ArrayMap<String, Boolean> mNoisy; private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions; private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies; @@ -1746,7 +1755,8 @@ public abstract class NotificationListenerService extends Service { getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key), getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), getShowBadge(key), getUserSentiment(key), getHidden(key), - getAudiblyAlerted(key), getSmartActions(key), getSmartReplies(key)); + getAudiblyAlerted(key), getNoisy(key), getSmartActions(key), + getSmartReplies(key)); return rank >= 0; } @@ -1894,6 +1904,16 @@ public abstract class NotificationListenerService extends Service { return audiblyAlerted == null ? false : audiblyAlerted.booleanValue(); } + private boolean getNoisy(String key) { + synchronized (this) { + if (mNoisy == null) { + buildNoisyLocked(); + } + } + Boolean noisy = mNoisy.get(key); + return noisy == null ? false : noisy.booleanValue(); + } + private ArrayList<Notification.Action> getSmartActions(String key) { synchronized (this) { if (mSmartActions == null) { @@ -2034,6 +2054,11 @@ public abstract class NotificationListenerService extends Service { } // Locked by 'this' + private void buildNoisyLocked() { + mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy()); + } + + // Locked by 'this' private void buildSmartActions() { Bundle smartActions = mRankingUpdate.getSmartActions(); mSmartActions = new ArrayMap<>(smartActions.size()); diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index b561bfd98038..f80df93364f4 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -40,13 +40,14 @@ public class NotificationRankingUpdate implements Parcelable { private final Bundle mSmartActions; private final Bundle mSmartReplies; private final Bundle mAudiblyAlerted; + private final Bundle mNoisy; public NotificationRankingUpdate(String[] keys, String[] interceptedKeys, Bundle visibilityOverrides, Bundle suppressedVisualEffects, int[] importance, Bundle explanation, Bundle overrideGroupKeys, Bundle channels, Bundle overridePeople, Bundle snoozeCriteria, Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions, - Bundle smartReplies, Bundle audiblyAlerted) { + Bundle smartReplies, Bundle audiblyAlerted, Bundle noisy) { mKeys = keys; mInterceptedKeys = interceptedKeys; mVisibilityOverrides = visibilityOverrides; @@ -63,6 +64,7 @@ public class NotificationRankingUpdate implements Parcelable { mSmartActions = smartActions; mSmartReplies = smartReplies; mAudiblyAlerted = audiblyAlerted; + mNoisy = noisy; } public NotificationRankingUpdate(Parcel in) { @@ -83,6 +85,7 @@ public class NotificationRankingUpdate implements Parcelable { mSmartActions = in.readBundle(); mSmartReplies = in.readBundle(); mAudiblyAlerted = in.readBundle(); + mNoisy = in.readBundle(); } @Override @@ -108,6 +111,7 @@ public class NotificationRankingUpdate implements Parcelable { out.writeBundle(mSmartActions); out.writeBundle(mSmartReplies); out.writeBundle(mAudiblyAlerted); + out.writeBundle(mNoisy); } public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR @@ -184,4 +188,8 @@ public class NotificationRankingUpdate implements Parcelable { public Bundle getAudiblyAlerted() { return mAudiblyAlerted; } + + public Bundle getNoisy() { + return mNoisy; + } } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index b047ef7af032..67505e58cf17 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -45,7 +45,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put("settings_dynamic_homepage", "false"); - DEFAULT_FLAGS.put("settings_mobile_network_v2", "false"); + DEFAULT_FLAGS.put("settings_mobile_network_v2", "true"); DEFAULT_FLAGS.put("settings_data_usage_v2", "false"); DEFAULT_FLAGS.put("settings_seamless_transfer", "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); diff --git a/core/java/com/android/internal/util/function/HeptConsumer.java b/core/java/com/android/internal/util/function/HeptConsumer.java new file mode 100644 index 000000000000..171b0f24b5be --- /dev/null +++ b/core/java/com/android/internal/util/function/HeptConsumer.java @@ -0,0 +1,28 @@ +/* + * 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.android.internal.util.function; + +import java.util.function.Consumer; + +/** + * A 7-argument {@link Consumer} + * + * @hide + */ +public interface HeptConsumer<A, B, C, D, E, F, G> { + void accept(A a, B b, C c, D d, E e, F f, G g); +} diff --git a/core/java/com/android/internal/util/function/HeptFunction.java b/core/java/com/android/internal/util/function/HeptFunction.java new file mode 100644 index 000000000000..7aa434575038 --- /dev/null +++ b/core/java/com/android/internal/util/function/HeptFunction.java @@ -0,0 +1,28 @@ +/* + * 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.android.internal.util.function; + +import java.util.function.Function; + +/** + * A 7-argument {@link Function} + * + * @hide + */ +public interface HeptFunction<A, B, C, D, E, F, G, R> { + R apply(A a, B b, C c, D d, E e, F f, G g); +} diff --git a/core/java/com/android/internal/util/function/HeptPredicate.java b/core/java/com/android/internal/util/function/HeptPredicate.java new file mode 100644 index 000000000000..531e53a992bf --- /dev/null +++ b/core/java/com/android/internal/util/function/HeptPredicate.java @@ -0,0 +1,28 @@ +/* + * 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.android.internal.util.function; + +import java.util.function.Predicate; + +/** + * A 7-argument {@link Predicate} + * + * @hide + */ +public interface HeptPredicate<A, B, C, D, E, F, G> { + boolean test(A a, B b, C c, D d, E e, F f, G g); +} diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java index 9378869d6934..4ffe44194958 100755 --- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java +++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java @@ -18,6 +18,8 @@ package com.android.internal.util.function.pooled; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.util.FunctionalUtils.ThrowingSupplier; +import com.android.internal.util.function.HeptConsumer; +import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadConsumer; @@ -37,59 +39,61 @@ import java.util.function.Function; * * @hide */ -abstract class OmniFunction<A, B, C, D, E, F, R> implements +abstract class OmniFunction<A, B, C, D, E, F, G, R> implements PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>, QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>, - HexFunction<A, B, C, D, E, F, R>, PooledConsumer<A>, BiConsumer<A, B>, - TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, QuintConsumer<A, B, C, D, E>, - HexConsumer<A, B, C, D, E, F>, PooledPredicate<A>, BiPredicate<A, B>, + HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>, + PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, + QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>, + HeptConsumer<A, B, C, D, E, F, G>, + PooledPredicate<A>, BiPredicate<A, B>, PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>, PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble { - abstract R invoke(A a, B b, C c, D d, E e, F f); + abstract R invoke(A a, B b, C c, D d, E e, F f, G g); @Override public R apply(A o, B o2) { - return invoke(o, o2, null, null, null, null); + return invoke(o, o2, null, null, null, null, null); } @Override public R apply(A o) { - return invoke(o, null, null, null, null, null); + return invoke(o, null, null, null, null, null, null); } - abstract public <V> OmniFunction<A, B, C, D, E, F, V> andThen( + public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen( Function<? super R, ? extends V> after); - abstract public OmniFunction<A, B, C, D, E, F, R> negate(); + public abstract OmniFunction<A, B, C, D, E, F, G, R> negate(); @Override public void accept(A o, B o2) { - invoke(o, o2, null, null, null, null); + invoke(o, o2, null, null, null, null, null); } @Override public void accept(A o) { - invoke(o, null, null, null, null, null); + invoke(o, null, null, null, null, null, null); } @Override public void run() { - invoke(null, null, null, null, null, null); + invoke(null, null, null, null, null, null, null); } @Override public R get() { - return invoke(null, null, null, null, null, null); + return invoke(null, null, null, null, null, null, null); } @Override public boolean test(A o, B o2) { - return (Boolean) invoke(o, o2, null, null, null, null); + return (Boolean) invoke(o, o2, null, null, null, null, null); } @Override public boolean test(A o) { - return (Boolean) invoke(o, null, null, null, null, null); + return (Boolean) invoke(o, null, null, null, null, null, null); } @Override @@ -104,42 +108,52 @@ abstract class OmniFunction<A, B, C, D, E, F, R> implements @Override public R apply(A a, B b, C c) { - return invoke(a, b, c, null, null, null); + return invoke(a, b, c, null, null, null, null); } @Override public void accept(A a, B b, C c) { - invoke(a, b, c, null, null, null); + invoke(a, b, c, null, null, null, null); } @Override public R apply(A a, B b, C c, D d) { - return invoke(a, b, c, d, null, null); + return invoke(a, b, c, d, null, null, null); } @Override public R apply(A a, B b, C c, D d, E e) { - return invoke(a, b, c, d, e, null); + return invoke(a, b, c, d, e, null, null); } @Override public R apply(A a, B b, C c, D d, E e, F f) { - return invoke(a, b, c, d, e, f); + return invoke(a, b, c, d, e, f, null); + } + + @Override + public R apply(A a, B b, C c, D d, E e, F f, G g) { + return invoke(a, b, c, d, e, f, g); } @Override public void accept(A a, B b, C c, D d) { - invoke(a, b, c, d, null, null); + invoke(a, b, c, d, null, null, null); } @Override public void accept(A a, B b, C c, D d, E e) { - invoke(a, b, c, d, e, null); + invoke(a, b, c, d, e, null, null); } @Override public void accept(A a, B b, C c, D d, E e, F f) { - invoke(a, b, c, d, e, f); + invoke(a, b, c, d, e, f, null); + } + + @Override + public void accept(A a, B b, C c, D d, E e, F f, G g) { + invoke(a, b, c, d, e, f, g); } @Override @@ -153,5 +167,5 @@ abstract class OmniFunction<A, B, C, D, E, F, R> implements } @Override - abstract public OmniFunction<A, B, C, D, E, F, R> recycleOnUse(); + public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse(); } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java index 15698cc67e51..af3c7527c432 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java @@ -21,6 +21,8 @@ import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquire import android.os.Message; +import com.android.internal.util.function.HeptConsumer; +import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadConsumer; @@ -174,7 +176,7 @@ public interface PooledLambda { Consumer<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null); } /** @@ -190,7 +192,7 @@ public interface PooledLambda { Predicate<? super A> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null); + function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null); } /** @@ -206,7 +208,7 @@ public interface PooledLambda { Function<? super A, ? extends R> function, A arg1) { return acquire(PooledLambdaImpl.sPool, - function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null); + function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null); } /** @@ -236,7 +238,7 @@ public interface PooledLambda { A arg1) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null); + function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -255,7 +257,7 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null); } /** @@ -272,7 +274,7 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null); + function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); } /** @@ -289,7 +291,7 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null); + function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); } /** @@ -306,7 +308,7 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null); } /** @@ -323,7 +325,7 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); } /** @@ -340,7 +342,7 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); } /** @@ -357,7 +359,7 @@ public interface PooledLambda { BiConsumer<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null); + function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null); } /** @@ -374,7 +376,7 @@ public interface PooledLambda { BiPredicate<? super A, ? super B> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null); + function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null); } /** @@ -391,7 +393,7 @@ public interface PooledLambda { BiFunction<? super A, ? super B, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2) { return acquire(PooledLambdaImpl.sPool, - function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null); + function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null); } /** @@ -422,7 +424,7 @@ public interface PooledLambda { A arg1, B arg2) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null); + function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -442,7 +444,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); } /** @@ -460,7 +462,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); + function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); } /** @@ -478,7 +480,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); } /** @@ -496,7 +498,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); } /** @@ -514,7 +516,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); } /** @@ -532,7 +534,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); } /** @@ -550,7 +552,7 @@ public interface PooledLambda { TriConsumer<? super A, ? super B, ? super C> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null); + function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); } /** @@ -568,7 +570,7 @@ public interface PooledLambda { TriFunction<? super A, ? super B, ? super C, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3) { return acquire(PooledLambdaImpl.sPool, - function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null); + function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null); } /** @@ -600,7 +602,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null); + function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -621,7 +623,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -640,7 +642,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); + function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -659,7 +661,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -678,7 +680,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -697,7 +699,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -716,7 +718,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -735,7 +737,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -754,7 +756,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -773,7 +775,7 @@ public interface PooledLambda { QuadConsumer<? super A, ? super B, ? super C, ? super D> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -792,7 +794,7 @@ public interface PooledLambda { QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function, A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) { return acquire(PooledLambdaImpl.sPool, - function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null); + function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null); } /** @@ -825,7 +827,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null); + function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -847,7 +849,7 @@ public interface PooledLambda { QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, - function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null); + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null); } /** @@ -867,7 +869,7 @@ public interface PooledLambda { QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5) { return acquire(PooledLambdaImpl.sPool, - function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null); + function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null); } /** @@ -902,7 +904,7 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null); + function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null); return Message.obtain().setCallback(callback.recycleOnUse()); } } @@ -925,7 +927,7 @@ public interface PooledLambda { HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, - function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6); + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null); } /** @@ -946,7 +948,7 @@ public interface PooledLambda { HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { return acquire(PooledLambdaImpl.sPool, - function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6); + function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null); } /** @@ -982,7 +984,91 @@ public interface PooledLambda { A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { synchronized (Message.sPoolSync) { PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, - function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6); + function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null); + return Message.obtain().setCallback(callback.recycleOnUse()); + } + } + + /** + * {@link PooledRunnable} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @return a {@link PooledRunnable}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) } + */ + static <A, B, C, D, E, F, G> PooledRunnable obtainRunnable( + HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { + return acquire(PooledLambdaImpl.sPool, + function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + + /** + * {@link PooledSupplier} factory + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @return a {@link PooledSupplier}, equivalent to lambda: + * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) } + */ + static <A, B, C, D, E, F, G, R> PooledSupplier<R> obtainSupplier( + HeptFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G, ? extends R> function, + A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { + return acquire(PooledLambdaImpl.sPool, + function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + + /** + * Factory of {@link Message}s that contain an + * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its + * {@link Message#getCallback internal callback}. + * + * The callback is equivalent to one obtainable via + * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)} + * + * Note that using this method with {@link android.os.Handler#handleMessage} + * is more efficient than the alternative of {@link android.os.Handler#post} + * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points + * when obtaining {@link Message} and {@link PooledRunnable} from pools separately + * + * You may optionally set a {@link Message#what} for the message if you want to be + * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise + * there's no need to do so + * + * @param function non-capturing lambda(typically an unbounded method reference) + * to be invoked on call + * @param arg1 parameter supplied to {@code function} on call + * @param arg2 parameter supplied to {@code function} on call + * @param arg3 parameter supplied to {@code function} on call + * @param arg4 parameter supplied to {@code function} on call + * @param arg5 parameter supplied to {@code function} on call + * @param arg6 parameter supplied to {@code function} on call + * @param arg7 parameter supplied to {@code function} on call + * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6, + * arg7) } when handled + */ + static <A, B, C, D, E, F, G> Message obtainMessage( + HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, + ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) { + synchronized (Message.sPoolSync) { + PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool, + function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7); return Message.obtain().setCallback(callback.recycleOnUse()); } } diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index 565ae1129cb4..eea1e5f0ac5c 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -24,6 +24,9 @@ import android.util.Pools; import com.android.internal.util.ArrayUtils; import com.android.internal.util.BitUtils; +import com.android.internal.util.function.HeptConsumer; +import com.android.internal.util.function.HeptFunction; +import com.android.internal.util.function.HeptPredicate; import com.android.internal.util.function.HexConsumer; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.HexPredicate; @@ -51,12 +54,12 @@ import java.util.function.Supplier; * @hide */ final class PooledLambdaImpl<R> extends OmniFunction<Object, - Object, Object, Object, Object, Object, R> { + Object, Object, Object, Object, Object, Object, R> { private static final boolean DEBUG = false; private static final String LOG_TAG = "PooledLambdaImpl"; - private static final int MAX_ARGS = 5; + private static final int MAX_ARGS = 7; private static final int MAX_POOL_SIZE = 50; @@ -122,7 +125,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, /** * Bit schema: - * AAAABCDEEEEEEFFFFFF + * AAAAAAABCDEEEEEEFFFFFF * * Where: * A - whether {@link #mArgs arg} at corresponding index was specified at @@ -158,17 +161,17 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { + R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { checkNotRecycled(); if (DEBUG) { Log.i(LOG_TAG, this + ".invoke(" + commaSeparateFirstN( - new Object[] { a1, a2, a3, a4, a5, a6 }, + new Object[] { a1, a2, a3, a4, a5, a6, a7 }, LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS))) + ")"); } final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) - && fillInArg(a4) && fillInArg(a5) && fillInArg(a6); + && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7); int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)); if (argCount != LambdaType.MASK_ARG_COUNT) { for (int i = 0; i < argCount; i++) { @@ -333,6 +336,27 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } } } + + case 7: { + switch (returnType) { + case LambdaType.ReturnType.VOID: { + ((HeptConsumer) mFunc).accept(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6)); + return null; + } + case LambdaType.ReturnType.BOOLEAN: { + return (R) (Object) ((HeptPredicate) mFunc).test(popArg(0), + popArg(1), popArg(2), popArg(3), + popArg(4), popArg(5), popArg(6)); + } + case LambdaType.ReturnType.OBJECT: { + return (R) ((HeptFunction) mFunc).apply(popArg(0), popArg(1), + popArg(2), popArg(3), popArg(4), + popArg(5), popArg(6)); + } + } + } } throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType)); } @@ -396,7 +420,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, */ static <E extends PooledLambda> E acquire(Pool pool, Object func, int fNumArgs, int numPlaceholders, int fReturnType, - Object a, Object b, Object c, Object d, Object e, Object f) { + Object a, Object b, Object c, Object d, Object e, Object f, Object g) { PooledLambdaImpl r = acquire(pool); if (DEBUG) { Log.i(LOG_TAG, @@ -411,6 +435,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, + ", d = " + d + ", e = " + e + ", f = " + f + + ", g = " + g + ")"); } r.mFunc = func; @@ -423,6 +448,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, setIfInBounds(r.mArgs, 3, d); setIfInBounds(r.mArgs, 4, e); setIfInBounds(r.mArgs, 5, f); + setIfInBounds(r.mArgs, 6, g); return (E) r; } @@ -448,12 +474,12 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - public OmniFunction<Object, Object, Object, Object, Object, Object, R> negate() { + public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() { throw new UnsupportedOperationException(); } @Override - public <V> OmniFunction<Object, Object, Object, Object, Object, Object, V> andThen( + public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen( Function<? super R, ? extends V> after) { throw new UnsupportedOperationException(); } @@ -474,7 +500,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } @Override - public OmniFunction<Object, Object, Object, Object, Object, Object, R> recycleOnUse() { + public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() { if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()"); mFlags |= FLAG_RECYCLE_ON_USE; return this; @@ -519,10 +545,10 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, * Contract for encoding a supported lambda type in {@link #MASK_BIT_COUNT} bits */ static class LambdaType { - public static final int MASK_ARG_COUNT = 0b111; - public static final int MASK_RETURN_TYPE = 0b111000; + public static final int MASK_ARG_COUNT = 0b1111; + public static final int MASK_RETURN_TYPE = 0b1110000; public static final int MASK = MASK_ARG_COUNT | MASK_RETURN_TYPE; - public static final int MASK_BIT_COUNT = 6; + public static final int MASK_BIT_COUNT = 7; static int encode(int argCount, int returnType) { return mask(MASK_ARG_COUNT, argCount) | mask(MASK_RETURN_TYPE, returnType); @@ -557,6 +583,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, case 4: return "Quad"; case 5: return "Quint"; case 6: return "Hex"; + case 7: return "Hept"; default: throw new IllegalArgumentException("" + argCount); } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index c96aaba60775..bdd5f83aa4ea 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -240,7 +240,7 @@ cc_library_shared { ], shared_libs: [ - "libbpf", + "libbpf_android", "libnetdbpf", "libnetdutils", "libmemtrack", diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml index 36efd0a28b91..c26bebe65a7a 100644 --- a/core/res/res/values-mcc302-mnc220/config.xml +++ b/core/res/res/values-mcc302-mnc220/config.xml @@ -33,7 +33,7 @@ <item>LPP_PROFILE=3</item> <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item> <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item> - <item>GPS_LOCK=0</item> + <item>GPS_LOCK=3</item> </string-array> </resources> diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml index a11dd95174a2..96338b589cd2 100644 --- a/core/res/res/values-mcc302-mnc221/config.xml +++ b/core/res/res/values-mcc302-mnc221/config.xml @@ -30,7 +30,7 @@ <item>LPP_PROFILE=3</item> <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item> <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item> - <item>GPS_LOCK=0</item> + <item>GPS_LOCK=3</item> </string-array> </resources> diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml index 8d29ec15bf6b..79f4bb6fdd0b 100644 --- a/core/res/res/values-mcc302-mnc370/config.xml +++ b/core/res/res/values-mcc302-mnc370/config.xml @@ -34,7 +34,7 @@ <item>LPP_PROFILE=2</item> <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item> <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item> - <item>GPS_LOCK=0</item> + <item>GPS_LOCK=3</item> </string-array> </resources> diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml index 650aa62b4a4e..10b007e9850d 100644 --- a/core/res/res/values-mcc302-mnc610/config.xml +++ b/core/res/res/values-mcc302-mnc610/config.xml @@ -32,7 +32,7 @@ <item>LPP_PROFILE=2</item> <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item> <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item> - <item>GPS_LOCK=0</item> + <item>GPS_LOCK=3</item> </string-array> </resources> diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml index 4bb68dcf04be..657d1e77c4ab 100644 --- a/core/res/res/values-mcc302-mnc640/config.xml +++ b/core/res/res/values-mcc302-mnc640/config.xml @@ -28,7 +28,7 @@ <item>LPP_PROFILE=2</item> <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item> <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item> - <item>GPS_LOCK=0</item> + <item>GPS_LOCK=3</item> </string-array> </resources> diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml index 735a8c80e4e8..ba8c75a179e9 100644 --- a/core/res/res/values-mcc302-mnc720/config.xml +++ b/core/res/res/values-mcc302-mnc720/config.xml @@ -34,7 +34,7 @@ <item>LPP_PROFILE=2</item> <item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item> <item>A_GLONASS_POS_PROTOCOL_SELECT=0</item> - <item>GPS_LOCK=0</item> + <item>GPS_LOCK=3</item> </string-array> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8d3992d20555..200c35d67b17 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -248,6 +248,10 @@ <item>@string/wfcSpnFormat_spn_wifi</item> <item>@string/wfcSpnFormat_wifi_calling_bar_spn</item> <item>@string/wfcSpnFormat_spn_vowifi</item> + <item>@string/wfcSpnFormat_wifi_calling</item> + <item>@string/wfcSpnFormat_wifi</item> + <item>@string/wfcSpnFormat_wifi_calling_wo_hyphen</item> + <item>@string/wfcSpnFormat_vowifi</item> </string-array> <!-- Spn during Wi-Fi Calling: "<operator>" --> @@ -264,6 +268,14 @@ <string name="wfcSpnFormat_wifi_calling_bar_spn">WiFi Calling | <xliff:g id="spn" example="Operator">%s</xliff:g></string> <!-- Spn during Wi-Fi Calling: "<operator> VoWifi" --> <string name="wfcSpnFormat_spn_vowifi"><xliff:g id="spn" example="Operator">%s</xliff:g> VoWifi</string> + <!-- Spn during Wi-Fi Calling: "Wi-Fi Calling" --> + <string name="wfcSpnFormat_wifi_calling">Wi-Fi Calling</string> + <!-- Spn during Wi-Fi Calling: "Wi-Fi" --> + <string name="wfcSpnFormat_wifi">Wi-Fi</string> + <!-- Spn during Wi-Fi Calling: "WiFi Calling" (without hyphen) --> + <string name="wfcSpnFormat_wifi_calling_wo_hyphen">WiFi Calling</string> + <!-- Spn during Wi-Fi Calling: "VoWifi" --> + <string name="wfcSpnFormat_vowifi">VoWifi</string> <!-- WFC, summary for Disabled --> <string name="wifi_calling_off_summary">Off</string> diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 0a4ac8cc5fec..c10e482f1d33 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -30,6 +30,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.security.KeyStoreException; import android.security.keymaster.ExportResult; import android.security.keymaster.KeyCharacteristics; import android.security.keymaster.KeymasterArguments; @@ -40,14 +41,21 @@ import android.security.keymaster.OperationResult; import android.security.keystore.KeyExpiredException; import android.security.keystore.KeyNotYetValidException; import android.security.keystore.KeyPermanentlyInvalidatedException; +import android.security.keystore.KeyProperties; +import android.security.keystore.KeyProtection; import android.security.keystore.StrongBoxUnavailableException; import android.security.keystore.UserNotAuthenticatedException; import android.util.Log; - +import com.android.org.bouncycastle.asn1.ASN1InputStream; +import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import java.math.BigInteger; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.security.InvalidKeyException; import java.util.List; import java.util.Locale; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; /** * @hide This should not be made public in its present form because it @@ -69,6 +77,7 @@ public class KeyStore { public static final int VALUE_CORRUPTED = 8; public static final int UNDEFINED_ACTION = 9; public static final int WRONG_PASSWORD = 10; + public static final int KEY_ALREADY_EXISTS = 16; public static final int CANNOT_ATTEST_IDS = -66; public static final int HARDWARE_TYPE_UNAVAILABLE = -68; @@ -239,7 +248,12 @@ public class KeyStore { if (value == null) { value = new byte[0]; } - return mBinder.insert(key, value, uid, flags); + int error = mBinder.insert(key, value, uid, flags); + if (error == KEY_ALREADY_EXISTS) { + mBinder.del(key, uid); + error = mBinder.insert(key, value, uid, flags); + } + return error; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; @@ -366,53 +380,6 @@ public class KeyStore { return isEmpty(UserHandle.myUserId()); } - public boolean generate(String key, int uid, int keyType, int keySize, int flags, - byte[][] args) { - try { - return mBinder.generate(key, uid, keyType, keySize, flags, - new KeystoreArguments(args)) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - public boolean importKey(String keyName, byte[] key, int uid, int flags) { - try { - return mBinder.import_key(keyName, key, uid, flags) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - public byte[] sign(String key, byte[] data) { - try { - return mBinder.sign(key, data); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return null; - } catch (android.os.ServiceSpecificException e) { - Log.w(TAG, "KeyStore exception", e); - return null; - } - - } - - public boolean verify(String key, byte[] data, byte[] signature) { - try { - signature = signature != null ? signature : new byte[0]; - return mBinder.verify(key, data, signature) == NO_ERROR; - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } catch (android.os.ServiceSpecificException e) { - Log.w(TAG, "KeyStore exception", e); - return false; - } - - } - public String grant(String key, int uid) { try { String grantAlias = mBinder.grant(key, uid); @@ -496,7 +463,12 @@ public class KeyStore { try { entropy = entropy != null ? entropy : new byte[0]; args = args != null ? args : new KeymasterArguments(); - return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics); + int error = mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics); + if (error == KEY_ALREADY_EXISTS) { + mBinder.del(alias, uid); + error = mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics); + } + return error; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; @@ -528,8 +500,14 @@ public class KeyStore { public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData, int uid, int flags, KeyCharacteristics outCharacteristics) { try { - return mBinder.importKey(alias, args, format, keyData, uid, flags, + int error = mBinder.importKey(alias, args, format, keyData, uid, flags, outCharacteristics); + if (error == KEY_ALREADY_EXISTS) { + mBinder.del(alias, uid); + error = mBinder.importKey(alias, args, format, keyData, uid, flags, + outCharacteristics); + } + return error; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; @@ -541,13 +519,78 @@ public class KeyStore { return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics); } + private String getAlgorithmFromPKCS8(byte[] keyData) { + try { + final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData)); + final PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject()); + final String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); + return new AlgorithmId(new ObjectIdentifier(algOid)).getName(); + } catch (IOException e) { + Log.e(TAG, "getAlgorithmFromPKCS8 Failed to parse key data"); + Log.e(TAG, Log.getStackTraceString(e)); + return null; + } + } + + private KeymasterArguments makeLegacyArguments(String algorithm) { + KeymasterArguments args = new KeymasterArguments(); + args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, + KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(algorithm)); + args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN); + args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_VERIFY); + args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT); + args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT); + args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE); + if (algorithm.equalsIgnoreCase(KeyProperties.KEY_ALGORITHM_RSA)) { + args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP); + args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT); + args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN); + args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PSS); + } + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE); + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5); + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1); + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224); + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256); + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384); + args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512); + args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); + args.addUnsignedLong(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, + KeymasterArguments.UINT64_MAX_VALUE); + args.addUnsignedLong(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, + KeymasterArguments.UINT64_MAX_VALUE); + args.addUnsignedLong(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, BigInteger.ZERO); + return args; + } + + public boolean importKey(String alias, byte[] keyData, int uid, int flags) { + String algorithm = getAlgorithmFromPKCS8(keyData); + if (algorithm == null) return false; + KeymasterArguments args = makeLegacyArguments(algorithm); + KeyCharacteristics out = new KeyCharacteristics(); + int result = importKey(alias, args, KeymasterDefs.KM_KEY_FORMAT_PKCS8, keyData, uid, + flags, out); + if (result != NO_ERROR) { + Log.e(TAG, Log.getStackTraceString( + new KeyStoreException(result, "legacy key import failed"))); + return false; + } + return true; + } + public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey, String wrappingKeyAlias, byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid, KeyCharacteristics outCharacteristics) { try { - return mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, + int error = mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, maskingKey, args, rootSid, fingerprintSid, outCharacteristics); + if (error == KEY_ALREADY_EXISTS) { + mBinder.del(wrappedKeyAlias, -1); + error = mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, + maskingKey, args, rootSid, fingerprintSid, outCharacteristics); + } + return error; } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); return SYSTEM_ERROR; @@ -627,21 +670,6 @@ public class KeyStore { } /** - * Check if the operation referenced by {@code token} is currently authorized. - * - * @param token An operation token returned by a call to - * {@link #begin(String, int, boolean, KeymasterArguments, byte[], KeymasterArguments) begin}. - */ - public boolean isOperationAuthorized(IBinder token) { - try { - return mBinder.isOperationAuthorized(token); - } catch (RemoteException e) { - Log.w(TAG, "Cannot connect to keystore", e); - return false; - } - } - - /** * Add an authentication record to the keystore authorization table. * * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster. diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 2152e1edc6f5..f179bc3f9d3d 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -16,8 +16,8 @@ package android.location; -import android.annotation.TestApi; import android.annotation.IntDef; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -685,6 +685,12 @@ public final class GnssMeasurement implements Parcelable { * * <p>This includes ensuring that all half-cycle ambiguities are resolved before this value is * reported as {@link #ADR_STATE_VALID}. + * + * <p>The alignment of the phase measurement will not be adjusted by the receiver so the + * in-phase and quadrature phase components will have a quarter cycle offset as they do when + * transmitted from the satellites. If the measurement is from a combination of the in-phase + * and quadrature phase components, then the alignment of the phase measurement will be aligned + * to the in-phase component. */ public double getAccumulatedDeltaRangeMeters() { return mAccumulatedDeltaRangeMeters; diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index f138685e9810..c86ebe70db5c 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -152,6 +152,13 @@ android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" style="@style/TextAppearance.NotificationInfo.Button" /> <TextView + android:id="@+id/toggle_silent" + android:text="@string/inline_silent_button_silent" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" + style="@style/TextAppearance.NotificationInfo.Button" /> + <TextView android:id="@+id/keep" android:minWidth="48dp" android:text="@string/inline_keep_button" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6f5d6577f700..50454fc9bcf2 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1540,6 +1540,12 @@ <!-- Notification inline controls: Shown when a channel's notifications are minimized --> <string name="notification_channel_minimized">These notifications will be minimized</string> + <!-- Notification inline controls: Shown when a channel's notifications are silenced [CHAR_LIMIT=100] --> + <string name="notification_channel_silenced">These notifications will be shown silently</string> + + <!-- Notification inline controls: Shown when a channel's notifications are set to alert [CHAR_LIMIT=100] --> + <string name="notification_channel_unsilenced">These notifications will alert you</string> + <!-- Notification Inline controls: continue receiving notifications prompt, channel level --> <string name="inline_blocking_helper">You usually dismiss these notifications. \nKeep showing them?</string> @@ -1556,6 +1562,12 @@ <!-- Notification inline controls: minimize notifications button --> <string name="inline_minimize_button">Minimize</string> + <!-- Notification inline controls: show notifications silently button [CHAR_LIMIT=25] --> + <string name="inline_silent_button_silent">Show silently</string> + + <!-- Notification inline controls: show and alert button [CHAR_LIMIT=25] --> + <string name="inline_silent_button_alert">Show and alert</string> + <!-- Notification Inline controls: continue receiving notifications prompt, app level --> <string name="inline_keep_showing_app">Keep showing notifications from this app?</string> diff --git a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java index d6472b7ddbb9..d294012a7527 100644 --- a/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java +++ b/packages/SystemUI/src/com/android/systemui/analytics/SensorLoggerSession.java @@ -16,17 +16,17 @@ package com.android.systemui.analytics; +import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session; +import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent; +import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.SensorEvent; +import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.TouchEvent; + import android.os.Build; import android.util.Log; import android.view.MotionEvent; import java.util.ArrayList; -import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session; -import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.PhoneEvent; -import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.SensorEvent; -import static com.android.systemui.statusbar.phone.nano.TouchAnalyticsProto.Session.TouchEvent; - /** * Collects touch, sensor and phone events and converts the data to * TouchAnalyticsProto.Session. @@ -104,6 +104,7 @@ public class SensorLoggerSession { proto.startTimestampMillis = mStartTimestampMillis; proto.durationMillis = mEndTimestampMillis - mStartTimestampMillis; proto.build = Build.FINGERPRINT; + proto.deviceId = Build.DEVICE; proto.result = mResult; proto.type = mType; proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java index b84b77c6d105..fb62baa5c63c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -102,6 +102,8 @@ public class NotificationData { public StatusBarNotification notification; public NotificationChannel channel; public boolean audiblyAlerted; + public boolean noisy; + public int importance; public StatusBarIconView icon; public StatusBarIconView expandedIcon; public ExpandableNotificationRow row; // the outer expanded view @@ -140,6 +142,16 @@ public class NotificationData { */ private boolean hasSentReply; + /** + * Whether this notification should be displayed as a bubble. + */ + private boolean mIsBubble; + + /** + * Whether the user has dismissed this notification when it was in bubble form. + */ + private boolean mUserDismissedBubble; + public Entry(StatusBarNotification n) { this(n, null); } @@ -155,6 +167,8 @@ public class NotificationData { public void populateFromRanking(@NonNull Ranking ranking) { channel = ranking.getChannel(); audiblyAlerted = ranking.audiblyAlerted(); + noisy = ranking.isNoisy(); + importance = ranking.getImportance(); snoozeCriteria = ranking.getSnoozeCriteria(); userSentiment = ranking.getUserSentiment(); smartActions = ranking.getSmartActions() == null @@ -172,6 +186,22 @@ public class NotificationData { return interruption; } + public void setIsBubble(boolean bubbleable) { + mIsBubble = bubbleable; + } + + public boolean isBubble() { + return mIsBubble; + } + + public void setBubbleDismissed(boolean userDismissed) { + mUserDismissedBubble = userDismissed; + } + + public boolean isBubbleDismissed() { + return mUserDismissedBubble; + } + /** * Resets the notification entry to be re-used. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java index 28c07a34beed..43b5503682cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java @@ -30,6 +30,9 @@ public class NotificationCounters { /** Counter tag for when the user hits 'stop notifications' in the blocking helper. */ public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS = "blocking_helper_stop_notifications"; + /** Counter tag for when the user hits 'show silently' in the blocking helper. */ + public static final String BLOCKING_HELPER_TOGGLE_SILENT = + "blocking_helper_toggle_silent"; /** Counter tag for when the user hits 'keep showing' in the blocking helper. */ public static final String BLOCKING_HELPER_KEEP_SHOWING = "blocking_helper_keep_showing"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 0a197daf9186..fbe9c5d40beb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -23,7 +23,6 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; -import androidx.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -31,6 +30,8 @@ import android.view.ViewAnimationUtils; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; @@ -98,6 +99,11 @@ public class NotificationGuts extends FrameLayout { * Return whether something changed and needs to be saved, possibly requiring a bouncer. */ boolean shouldBeSaved(); + + /** + * Called when the guts view has finished its close animation. + */ + default void onFinishedClosing() {} } public interface OnGutsClosedListener { @@ -304,7 +310,7 @@ public class NotificationGuts extends FrameLayout { x, y, r, 0); a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - a.addListener(new AnimateCloseListener(this /* view */)); + a.addListener(new AnimateCloseListener(this /* view */, mGutsContent)); a.start(); } else { // Fade in the blocking helper. @@ -312,11 +318,12 @@ public class NotificationGuts extends FrameLayout { .alpha(0f) .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE) .setInterpolator(Interpolators.ALPHA_OUT) - .setListener(new AnimateCloseListener(this /* view */)) + .setListener(new AnimateCloseListener(this, /* view */mGutsContent)) .start(); } } else { Log.w(TAG, "Failed to animate guts close"); + mGutsContent.onFinishedClosing(); } } @@ -414,15 +421,18 @@ public class NotificationGuts extends FrameLayout { /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */ private static class AnimateCloseListener extends AnimatorListenerAdapter { final View mView; + private final GutsContent mGutsContent; - private AnimateCloseListener(View view) { + private AnimateCloseListener(View view, GutsContent gutsContent) { mView = view; + mGutsContent = gutsContent; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mView.setVisibility(View.GONE); + mGutsContent.onFinishedClosing(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 24999525ab72..b838c9b5482d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; @@ -46,15 +45,13 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; @@ -298,7 +295,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mDeviceProvisionedController.isDeviceProvisioned(), row.getIsNonblockable(), isForBlockingHelper, - row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE); + row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE, + row.getEntry().noisy, + row.getEntry().importance); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 903c27277b70..522da4d51470 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -16,13 +16,18 @@ package com.android.systemui.statusbar.notification.row; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.IntDef; import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; @@ -54,6 +59,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import java.util.List; @@ -65,6 +71,17 @@ import java.util.List; public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent { private static final String TAG = "InfoGuts"; + @IntDef(prefix = { "SWAP_CONTENT_" }, value = { + SWAP_CONTENT_UNDO, + SWAP_CONTENT_TOGGLE_SILENT, + SWAP_CONTENT_BLOCK, + }) + @interface SwapContentAction {} + + private static final int SWAP_CONTENT_UNDO = 0; + private static final int SWAP_CONTENT_TOGGLE_SILENT = 1; + private static final int SWAP_CONTENT_BLOCK = 2; + private INotificationManager mINotificationManager; private PackageManager mPm; private MetricsLogger mMetricsLogger; @@ -74,7 +91,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private int mAppUid; private int mNumUniqueChannelsInRow; private NotificationChannel mSingleNotificationChannel; - private int mStartingUserImportance; + private int mStartingChannelImportance; + private int mStartingChannelOrNotificationImportance; private int mChosenImportance; private boolean mIsSingleDefaultChannel; private boolean mIsNonblockable; @@ -82,6 +100,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private AnimatorSet mExpandAnimation; private boolean mIsForeground; private boolean mIsDeviceProvisioned; + private boolean mIsNoisy; private CheckSaveListener mCheckSaveListener; private OnSettingsClickListener mOnSettingsClickListener; @@ -102,10 +121,22 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G closeControls(v); }; + private OnClickListener mOnToggleSilent = v -> { + Runnable saveImportance = () -> { + mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT; + swapContent(SWAP_CONTENT_TOGGLE_SILENT); + }; + if (mCheckSaveListener != null) { + mCheckSaveListener.checkSave(saveImportance, mSbn); + } else { + saveImportance.run(); + } + }; + private OnClickListener mOnStopOrMinimizeNotifications = v -> { Runnable saveImportance = () -> { mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS; - swapContent(false); + swapContent(SWAP_CONTENT_BLOCK); }; if (mCheckSaveListener != null) { mCheckSaveListener.checkSave(saveImportance, mSbn); @@ -118,7 +149,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // Reset exit counter that we'll log and record an undo event separately (not an exit event) mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO); - swapContent(true); + swapContent(SWAP_CONTENT_UNDO); }; public NotificationInfo(Context context, AttributeSet attrs) { @@ -152,12 +183,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final OnSettingsClickListener onSettingsClick, final OnAppSettingsClickListener onAppSettingsClick, boolean isDeviceProvisioned, - boolean isNonblockable) + boolean isNonblockable, + boolean isNoisy, + int importance) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, onAppSettingsClick, isDeviceProvisioned, isNonblockable, - false /* isBlockingHelper */, false /* isUserSentimentNegative */); + false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy, + importance); } public void bindNotification( @@ -173,7 +207,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isDeviceProvisioned, boolean isNonblockable, boolean isForBlockingHelper, - boolean isUserSentimentNegative) + boolean isUserSentimentNegative, + boolean isNoisy, + int importance) throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -186,7 +222,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mCheckSaveListener = checkSaveListener; mOnSettingsClickListener = onSettingsClick; mSingleNotificationChannel = notificationChannel; - mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); + int channelImportance = mSingleNotificationChannel.getImportance(); + mStartingChannelImportance = mChosenImportance = channelImportance; + mStartingChannelOrNotificationImportance = + channelImportance == IMPORTANCE_UNSPECIFIED ? importance : channelImportance; mNegativeUserSentiment = isUserSentimentNegative; mIsNonblockable = isNonblockable; mIsForeground = @@ -194,6 +233,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mIsForBlockingHelper = isForBlockingHelper; mAppUid = mSbn.getUid(); mIsDeviceProvisioned = isDeviceProvisioned; + mIsNoisy = isNoisy; int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); @@ -306,7 +346,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private boolean hasImportanceChanged() { - return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance; + return mSingleNotificationChannel != null + && mStartingChannelImportance != mChosenImportance; } private void saveImportance() { @@ -320,34 +361,46 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G */ private void updateImportance() { MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, - mChosenImportance - mStartingUserImportance); + mChosenImportance - mStartingChannelImportance); Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, - mStartingUserImportance, mChosenImportance)); + mStartingChannelImportance, mChosenImportance)); } private void bindButtons() { // Set up stay-in-notification actions View block = findViewById(R.id.block); TextView keep = findViewById(R.id.keep); + TextView silent = findViewById(R.id.toggle_silent); View minimize = findViewById(R.id.minimize); findViewById(R.id.undo).setOnClickListener(mOnUndo); block.setOnClickListener(mOnStopOrMinimizeNotifications); keep.setOnClickListener(mOnKeepShowing); + silent.setOnClickListener(mOnToggleSilent); minimize.setOnClickListener(mOnStopOrMinimizeNotifications); if (mIsNonblockable) { keep.setText(android.R.string.ok); block.setVisibility(GONE); + silent.setVisibility(GONE); minimize.setVisibility(GONE); } else if (mIsForeground) { block.setVisibility(GONE); + silent.setVisibility(GONE); minimize.setVisibility(VISIBLE); - } else if (!mIsForeground) { + } else { block.setVisibility(VISIBLE); + boolean showToggleSilent = mIsNoisy + && NotificationUtils.useNewInterruptionModel(mContext); + silent.setVisibility(showToggleSilent ? VISIBLE : GONE); + boolean isCurrentlyAlerting = + mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT; + silent.setText(isCurrentlyAlerting + ? R.string.inline_silent_button_silent + : R.string.inline_silent_button_alert); minimize.setVisibility(GONE); } @@ -368,7 +421,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private void swapContent(boolean showPrompt) { + private void swapContent(@SwapContentAction int action) { if (mExpandAnimation != null) { mExpandAnimation.cancel(); } @@ -378,26 +431,43 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G TextView confirmationText = findViewById(R.id.confirmation_text); View header = findViewById(R.id.header); - if (showPrompt) { - mChosenImportance = mStartingUserImportance; - } else if (mIsForeground) { - mChosenImportance = IMPORTANCE_MIN; - confirmationText.setText(R.string.notification_channel_minimized); - } else { - mChosenImportance = IMPORTANCE_NONE; - confirmationText.setText(R.string.notification_channel_disabled); + switch (action) { + case SWAP_CONTENT_UNDO: + mChosenImportance = mStartingChannelImportance; + break; + case SWAP_CONTENT_TOGGLE_SILENT: + if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) { + mChosenImportance = IMPORTANCE_LOW; + confirmationText.setText(R.string.notification_channel_silenced); + } else { + mChosenImportance = IMPORTANCE_HIGH; + confirmationText.setText(R.string.notification_channel_unsilenced); + } + break; + case SWAP_CONTENT_BLOCK: + if (mIsForeground) { + mChosenImportance = IMPORTANCE_MIN; + confirmationText.setText(R.string.notification_channel_minimized); + } else { + mChosenImportance = IMPORTANCE_NONE; + confirmationText.setText(R.string.notification_channel_disabled); + } + break; + default: + throw new IllegalArgumentException(); } + boolean isUndo = action == SWAP_CONTENT_UNDO; ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA, - prompt.getAlpha(), showPrompt ? 1f : 0f); - promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); + prompt.getAlpha(), isUndo ? 1f : 0f); + promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA, - confirmation.getAlpha(), showPrompt ? 0f : 1f); - confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); + confirmation.getAlpha(), isUndo ? 0f : 1f); + confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); - prompt.setVisibility(showPrompt ? VISIBLE : GONE); - confirmation.setVisibility(showPrompt ? GONE : VISIBLE); - header.setVisibility(showPrompt ? VISIBLE : GONE); + prompt.setVisibility(isUndo ? VISIBLE : GONE); + confirmation.setVisibility(isUndo ? GONE : VISIBLE); + header.setVisibility(isUndo ? VISIBLE : GONE); mExpandAnimation = new AnimatorSet(); mExpandAnimation.playTogether(promptAnim, confirmAnim); @@ -413,8 +483,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @Override public void onAnimationEnd(Animator animation) { if (!cancelled) { - prompt.setVisibility(showPrompt ? VISIBLE : GONE); - confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + prompt.setVisibility(isUndo ? VISIBLE : GONE); + confirmation.setVisibility(isUndo ? GONE : VISIBLE); } } }); @@ -428,6 +498,25 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } @Override + public void onFinishedClosing() { + mStartingChannelImportance = mChosenImportance; + if (mChosenImportance != IMPORTANCE_UNSPECIFIED) { + mStartingChannelOrNotificationImportance = mChosenImportance; + } + mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; + + View prompt = findViewById(R.id.prompt); + ViewGroup confirmation = findViewById(R.id.confirmation); + View header = findViewById(R.id.header); + prompt.setVisibility(VISIBLE); + prompt.setAlpha(1f); + confirmation.setVisibility(GONE); + confirmation.setAlpha(1f); + header.setVisibility(VISIBLE); + header.setAlpha(1f); + } + + @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); if (mGutsContainer != null && diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index c3bf16e0f796..a315bf3c2d99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -1124,7 +1124,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) private float getExpandTranslationStart() { - return -mTopPadding + getMinExpansionHeight(); + return -mTopPadding + getMinExpansionHeight() - mShelf.getIntrinsicHeight(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto index 50fd52ae0070..cfb633d61bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/touch_analytics.proto @@ -134,4 +134,6 @@ message Session { optional int32 touchAreaHeight = 10; optional Type type = 11; repeated PhoneEvent phoneEvents = 12; + + optional string device_id = 13; }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java index 0251f64fd6df..8e6bfe3a5f91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java @@ -437,15 +437,15 @@ public class NotificationDataTest extends SysuiTestCase { outRanking.getImportance(), outRanking.getImportanceExplanation(), outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, outRanking.canShowBadge(), outRanking.getUserSentiment(), true, - false, null, null); + false, false, null, null); } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) { outRanking.populate(key, outRanking.getRank(), outRanking.matchesInterruptionFilter(), outRanking.getVisibilityOverride(), 255, outRanking.getImportance(), outRanking.getImportanceExplanation(), outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, - outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false, null, - null); + outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false, + false, null, null); } else { outRanking.populate(key, outRanking.getRank(), outRanking.matchesInterruptionFilter(), @@ -453,8 +453,7 @@ public class NotificationDataTest extends SysuiTestCase { outRanking.getImportance(), outRanking.getImportanceExplanation(), outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null, outRanking.canShowBadge(), outRanking.getUserSentiment(), false, false, - null, - null); + false, null, null); } return true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 9c68e7d9ef3a..9f8a5cc0afdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -167,7 +167,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 0, NotificationManager.IMPORTANCE_DEFAULT, null, null, - null, null, null, true, sentiment, false, false, null, null); + null, null, null, true, sentiment, false, false, false, null, null); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } @@ -186,7 +186,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { null, null, null, null, null, true, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, false, - smartActions, null); + false, smartActions, null); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index ee35449e05da..626726d4aa4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -19,8 +19,10 @@ package com.android.systemui.statusbar.notification.row; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.service.notification.NotificationListenerService.Ranking + .USER_SENTIMENT_NEGATIVE; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -34,15 +36,14 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; -import android.app.NotificationManager; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Binder; @@ -57,11 +58,12 @@ import android.view.View; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager + .OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -71,8 +73,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; /** * Tests for {@link NotificationGutsManager}. @@ -84,7 +86,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; private NotificationChannel mTestNotificationChannel = new NotificationChannel( - TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); + TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); private TestableLooper mTestableLooper; private Handler mHandler; private NotificationTestHelper mHelper; @@ -297,7 +299,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(false), eq(true) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */); + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(0)); } @Test @@ -324,7 +328,69 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(false), eq(false) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */); + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(0)); + } + + @Test + public void testInitializeNotificationInfoView_noisy() throws Exception { + NotificationInfo notificationInfoView = mock(NotificationInfo.class); + ExpandableNotificationRow row = spy(mHelper.createRow()); + row.setBlockingHelperShowing(true); + row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + row.getEntry().noisy = true; + when(row.getIsNonblockable()).thenReturn(false); + StatusBarNotification statusBarNotification = row.getStatusBarNotification(); + + mGutsManager.initializeNotificationInfo(row, notificationInfoView); + + verify(notificationInfoView).bindNotification( + any(PackageManager.class), + any(INotificationManager.class), + eq(statusBarNotification.getPackageName()), + any(NotificationChannel.class), + anyInt(), + eq(statusBarNotification), + any(NotificationInfo.CheckSaveListener.class), + any(NotificationInfo.OnSettingsClickListener.class), + any(NotificationInfo.OnAppSettingsClickListener.class), + eq(false), + eq(false), + eq(true) /* isForBlockingHelper */, + eq(true) /* isUserSentimentNegative */, + eq(true) /*isNoisy */, + eq(0)); + } + + @Test + public void testInitializeNotificationInfoView_importance() throws Exception { + NotificationInfo notificationInfoView = mock(NotificationInfo.class); + ExpandableNotificationRow row = spy(mHelper.createRow()); + row.setBlockingHelperShowing(true); + row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + row.getEntry().importance = IMPORTANCE_DEFAULT; + when(row.getIsNonblockable()).thenReturn(false); + StatusBarNotification statusBarNotification = row.getStatusBarNotification(); + + mGutsManager.initializeNotificationInfo(row, notificationInfoView); + + verify(notificationInfoView).bindNotification( + any(PackageManager.class), + any(INotificationManager.class), + eq(statusBarNotification.getPackageName()), + any(NotificationChannel.class), + anyInt(), + eq(statusBarNotification), + any(NotificationInfo.CheckSaveListener.class), + any(NotificationInfo.OnSettingsClickListener.class), + any(NotificationInfo.OnAppSettingsClickListener.class), + eq(false), + eq(false), + eq(true) /* isForBlockingHelper */, + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(IMPORTANCE_DEFAULT)); } @Test @@ -352,7 +418,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true), eq(false), eq(false) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */); + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(0)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index ca968a8af85b..37441963e32d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -17,11 +17,14 @@ package com.android.systemui.statusbar.notification.row; import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME; +import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -56,6 +59,7 @@ import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.IBinder; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -72,6 +76,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -150,6 +155,15 @@ public class NotificationInfoTest extends SysuiTestCase { IMPORTANCE_LOW); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); + + Settings.Secure.putInt(mContext.getContentResolver(), + NOTIFICATION_NEW_INTERRUPTION_MODEL, 1); + } + + @After + public void tearDown() { + Settings.Secure.putInt(mContext.getContentResolver(), + NOTIFICATION_NEW_INTERRUPTION_MODEL, 0); } // TODO: if tests are taking too long replace this with something that makes the animation @@ -172,7 +186,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -184,7 +199,8 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -192,7 +208,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); @@ -208,7 +225,8 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -219,7 +237,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -228,7 +247,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -241,7 +260,7 @@ public class NotificationInfoTest extends SysuiTestCase { eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -249,7 +268,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -257,18 +277,71 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_BlockButton() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); + final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(VISIBLE, block.getVisibility()); + assertEquals(GONE, toggleSilent.getVisibility()); assertEquals(GONE, minimize.getVisibility()); } @Test + public void testBindNotification_SilenceButton() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText()); + } + + @Test + public void testBindNotification_UnSilenceButton() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_LOW); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText()); + } + + @Test + public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText()); + } + + @Test + public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_LOW); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText()); + } + + @Test public void testBindNotification_MinButton() throws Exception { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); @@ -283,7 +356,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, true, false); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -294,7 +367,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -306,7 +380,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); - }, null, false, false); + }, null, false, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -314,11 +388,12 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { - }, null, true, false); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -326,7 +401,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); } @@ -335,7 +411,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true, - true, true); + true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(1)).count(anyString(), anyInt()); } @@ -348,7 +424,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, true, true); + }, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -361,7 +437,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -372,7 +448,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -381,7 +457,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_BlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false, - true, true); + true, true, false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText()); @@ -390,7 +466,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -400,7 +477,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -410,7 +488,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -423,7 +502,8 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); mTestableLooper.processAllMessages(); @@ -432,11 +512,40 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testDoesNotUpdateNotificationChannelAfterImportanceChangedSilenced() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mTestableLooper.processAllMessages(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); + } + + @Test + public void testDoesNotUpdateNotificationChannelAfterImportanceChangedUnSilenced() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mTestableLooper.processAllMessages(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); + } + + @Test public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { int originalImportance = mNotificationChannel.getImportance(); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); @@ -450,7 +559,8 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -468,7 +578,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , - true, false /* isNonblockable */); + true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -489,7 +599,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , - true, false /* isNonblockable */); + true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -510,7 +620,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -538,7 +648,7 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -566,7 +676,8 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, - true, true /* isUserSentimentNegative */); + true, true /* isUserSentimentNegative */, false, /* isNoisy */ + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true /* save */, false /* force */); @@ -585,7 +696,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -607,7 +718,8 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, true, - false /* isUserSentimentNegative */); + false /* isUserSentimentNegative */, + false, /* isNoisy */IMPORTANCE_DEFAULT); NotificationGuts guts = mock(NotificationGuts.class); doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); mNotificationInfo.setGutsParent(guts); @@ -621,7 +733,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -634,7 +747,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -666,7 +780,8 @@ public class NotificationInfoTest extends SysuiTestCase { true /*provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, + false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -687,7 +802,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeMin() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -701,7 +817,8 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -721,7 +838,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testKeepUpdatesNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -738,7 +856,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -759,7 +878,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testMinUndoDoesNotMinNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -777,10 +897,97 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testSilenceCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); + } + + @Test + public void testUnSilenceCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); + } + + @Test + public void testSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); + } + + @Test + public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_LOW); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); + } + + @Test public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -795,7 +1002,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -812,7 +1020,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, null, null, true, true); + }, null, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -830,7 +1038,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false); + }, null, null, true, false, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -866,7 +1074,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -894,7 +1102,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -913,7 +1121,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, - null, true, false); + null, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -933,7 +1141,8 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -956,7 +1165,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true, - true, true); + true, true, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -972,7 +1181,8 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -984,7 +1194,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_block() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -993,10 +1204,39 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testUndoText_silence() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); + assertEquals(mContext.getString(R.string.notification_channel_silenced), + confirmationText.getText()); + } + + @Test + public void testUndoText_unsilence() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); + assertEquals(mContext.getString(R.string.notification_channel_unsilenced), + confirmationText.getText()); + } + + @Test public void testNoHeaderOnConfirmation() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1007,7 +1247,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHeaderOnUndo() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 32667b850888..5814064e5fbd 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.UID_STATE_BACKGROUND; import static android.app.AppOpsManager.UID_STATE_CACHED; import static android.app.AppOpsManager.UID_STATE_FOREGROUND; @@ -36,8 +37,11 @@ import android.app.AppOpsManager.HistoricalOpEntry; import android.app.AppOpsManager.HistoricalPackageOps; import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal.CheckOpsDelegate; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -645,6 +649,26 @@ public class AppOpsService extends IAppOpsService.Stub { } } + final IntentFilter packageSuspendFilter = new IntentFilter(); + packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); + packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); + final String[] changedPkgs = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_PACKAGE_LIST); + final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(OP_PLAY_AUDIO); + for (int i = 0; i < changedUids.length; i++) { + final int changedUid = changedUids[i]; + final String changedPkg = changedPkgs[i]; + // We trust packagemanager to insert matching uid and packageNames in the extras + mHandler.sendMessage(PooledLambda.obtainMessage(AppOpsService::notifyOpChanged, + AppOpsService.this, callbacks, OP_PLAY_AUDIO, changedUid, changedPkg)); + } + } + }, packageSuspendFilter); + PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); packageManagerInternal.setExternalSourcesPolicy( diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c52df2d5db82..c645e52a33fc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3046,35 +3046,18 @@ public class ActivityManagerService extends IActivityManager.Stub public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { - synchronized (this) { - /** - * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this - * call when called/invoked from the shell command. To avoid deadlock, we go ahead and - * acquire the AMS lock now since ATMS will need to synchronously call back into AMS - * later to modify process settings due to the flags. - * TODO(b/80414790): Investigate a better way of untangling this. - */ + return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId); - } } WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { - synchronized (this) { - /** - * Flags like {@link android.app.ActivityManager#START_FLAG_DEBUG} maybe be set on this - * call when called/invoked from the shell command. To avoid deadlock, we go ahead and - * acquire the AMS lock now since ATMS will need to synchronously call back into AMS - * later to modify process settings due to the flags. - * TODO(b/80414790): Investigate a better way of untangling this. - */ return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId); - } } @Override @@ -19166,22 +19149,30 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags, - ProfilerInfo profilerInfo) { + ProfilerInfo profilerInfo, Object wmLock) { synchronized (ActivityManagerService.this) { - if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) { - setDebugApp(aInfo.processName, true, false); - } + /** + * This function is called from the window manager context and needs to be executed + * synchronously. To avoid deadlock, we pass a message to AMS to execute the + * function and notify the passed in lock when it has been completed. + */ + synchronized (wmLock) { + if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) { + setDebugApp(aInfo.processName, true, false); + } - if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) { - setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName); - } + if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) { + setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName); + } - if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) { - setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName); - } + if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) { + setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName); + } - if (profilerInfo != null) { - setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo); + if (profilerInfo != null) { + setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo); + } + wmLock.notify(); } } } @@ -19473,7 +19464,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void startDelegateShellPermissionIdentity(int delegateUid) { + public void startDelegateShellPermissionIdentity(int delegateUid, + @Nullable String[] permissions) { if (UserHandle.getCallingAppId() != Process.SHELL_UID && UserHandle.getCallingAppId() != Process.ROOT_UID) { throw new SecurityException("Only the shell can delegate its permissions"); @@ -19492,11 +19484,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (!(mAppOpsService.getAppOpsServiceDelegate() instanceof ShellDelegate)) { throw new IllegalStateException("Bad shell delegate state"); } - if (((ShellDelegate) mAppOpsService.getAppOpsServiceDelegate()) - .getDelegateUid() != delegateUid) { + final ShellDelegate delegate = (ShellDelegate) mAppOpsService + .getAppOpsServiceDelegate(); + if (delegate.getDelegateUid() != delegateUid) { throw new SecurityException("Shell can delegate permissions only " + "to one instrumentation at a time"); } + delegate.setPermissions(permissions); return; } @@ -19514,7 +19508,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Hook them up... final ShellDelegate shellDelegate = new ShellDelegate( - instr.mTargetInfo.packageName, delegateUid); + instr.mTargetInfo.packageName, delegateUid, permissions); mAppOpsService.setAppOpsServiceDelegate(shellDelegate); getPackageManagerInternalLocked().setCheckPermissionDelegate(shellDelegate); return; @@ -19537,20 +19531,26 @@ public class ActivityManagerService extends IActivityManager.Stub private class ShellDelegate implements CheckOpsDelegate, CheckPermissionDelegate { private final String mTargetPackageName; private final int mTargetUid; + private @Nullable String[] mPermissions; - ShellDelegate(String targetPacakgeName, int targetUid) { + ShellDelegate(String targetPacakgeName, int targetUid, @Nullable String[] permissions) { mTargetPackageName = targetPacakgeName; mTargetUid = targetUid; + mPermissions = permissions; } int getDelegateUid() { return mTargetUid; } + void setPermissions(@Nullable String[] permissions) { + mPermissions = permissions; + } + @Override public int checkOperation(int code, int uid, String packageName, TriFunction<Integer, Integer, String, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(code, Process.SHELL_UID, @@ -19565,7 +19565,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkAudioOperation(int code, int usage, int uid, String packageName, QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { return superImpl.apply(code, usage, Process.SHELL_UID, @@ -19580,7 +19580,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int noteOperation(int code, int uid, String packageName, TriFunction<Integer, Integer, String, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID, @@ -19595,7 +19595,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkPermission(String permName, String pkgName, int userId, TriFunction<String, String, Integer, Integer> superImpl) { - if (mTargetPackageName.equals(pkgName)) { + if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) { return superImpl.apply(permName, "com.android.shell", userId); } return superImpl.apply(permName, pkgName, userId); @@ -19604,11 +19604,29 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkUidPermission(String permName, int uid, BiFunction<String, Integer, Integer> superImpl) { - if (uid == mTargetUid) { + if (uid == mTargetUid && isTargetPermission(permName)) { return superImpl.apply(permName, Process.SHELL_UID); } return superImpl.apply(permName, uid); } + + private boolean isTargetOp(int code) { + // null permissions means all ops are targeted + if (mPermissions == null) { + return true; + } + // no permission for the op means the op is targeted + final String permission = AppOpsManager.opToPermission(code); + if (permission == null) { + return true; + } + return isTargetPermission(permission); + } + + private boolean isTargetPermission(@NonNull String permission) { + // null permissions means all permissions are targeted + return (mPermissions == null || ArrayUtils.contains(mPermissions, permission)); + } } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index fe402eaa29b6..90180065461a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2105,8 +2105,13 @@ final class ActivityManagerShellCommand extends ShellCommand { } FeatureInfo[] features = pm.getSystemAvailableFeatures(); - Arrays.sort(features, (o1, o2) -> - (o1.name == o2.name ? 0 : (o1.name == null ? -1 : o1.name.compareTo(o2.name)))); + Arrays.sort(features, (o1, o2) -> { + if (o1.name == o2.name) return 0; + if (o1.name == null) return -1; + if (o2.name == null) return 1; + return o1.name.compareTo(o2.name); + }); + for (int i = 0; i < features.length; i++) { if (features[i].name != null) { if (protoOutputStream != null) { diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 0ee55ed2e832..527539d8ce0d 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.ICameraService; import android.hardware.ICameraServiceProxy; +import android.media.AudioManager; import android.metrics.LogMaker; import android.nfc.INfcAdapter; import android.os.Binder; @@ -393,6 +394,19 @@ public class CameraServiceProxy extends SystemService boolean wasEmpty = mActiveCameraUsage.isEmpty(); switch (newCameraState) { case ICameraServiceProxy.CAMERA_STATE_OPEN: + // Notify the audio subsystem about the facing of the most-recently opened + // camera This can be used to select the best audio tuning in case video + // recording with that camera will happen. Since only open events are used, if + // multiple cameras are opened at once, the one opened last will be used to + // select audio tuning. + AudioManager audioManager = getContext().getSystemService(AudioManager.class); + if (audioManager != null) { + // Map external to front for audio tuning purposes + String facingStr = (facing == ICameraServiceProxy.CAMERA_FACING_BACK) ? + "back" : "front"; + String facingParameter = "cameraFacing=" + facingStr; + audioManager.setParameters(facingParameter); + } break; case ICameraServiceProxy.CAMERA_STATE_ACTIVE: CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel); diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index deaa33485170..94c94a514dec 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -165,7 +165,7 @@ public class PermissionMonitor { } @VisibleForTesting - int getDeviceFirstSdkInt() { + protected int getDeviceFirstSdkInt() { return Build.VERSION.FIRST_SDK_INT; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f87a5f7a1a52..b404c41c211d 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6564,6 +6564,7 @@ public class NotificationManagerService extends SystemService { Bundle smartActions = new Bundle(); Bundle smartReplies = new Bundle(); Bundle audiblyAlerted = new Bundle(); + Bundle noisy = new Bundle(); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { @@ -6594,6 +6595,7 @@ public class NotificationManagerService extends SystemService { smartActions.putParcelableArrayList(key, record.getSmartActions()); smartReplies.putCharSequenceArrayList(key, record.getSmartReplies()); audiblyAlerted.putBoolean(key, record.getAudiblyAlerted()); + noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null); } final int M = keys.size(); String[] keysAr = keys.toArray(new String[M]); @@ -6605,7 +6607,7 @@ public class NotificationManagerService extends SystemService { return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden, - smartActions, smartReplies, audiblyAlerted); + smartActions, smartReplies, audiblyAlerted, noisy); } boolean hasCompanionDevice(ManagedServiceInfo info) { diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 753c2833b056..580e4f481a27 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -35,6 +35,7 @@ import android.util.Slog; import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; @@ -153,7 +154,7 @@ public class DexManager { * @param classPaths the class paths corresponding to the class loaders names from * {@param classLoadersNames}. The the first element corresponds to the first class loader * and so on. A classpath is represented as a list of dex files separated by - * {@code File.pathSeparator}. + * {@code File.pathSeparator}, or null if the class loader's classpath is not known. * The dex files found in the first class path will be recorded in the usage file. * @param loaderIsa the ISA of the app loading the dex files * @param loaderUserId the user id which runs the code loading the dex files @@ -169,7 +170,8 @@ public class DexManager { } } - private void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, + @VisibleForTesting + /*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, List<String> classLoaderNames, List<String> classPaths, String loaderIsa, int loaderUserId) { if (classLoaderNames.size() != classPaths.size()) { @@ -186,8 +188,14 @@ public class DexManager { return; } + // The first classpath should never be null because the first classloader + // should always be an instance of BaseDexClassLoader. + String firstClassPath = classPaths.get(0); + if (firstClassPath == null) { + return; + } // The classpath is represented as a list of dex files separated by File.pathSeparator. - String[] dexPathsToRegister = classPaths.get(0).split(File.pathSeparator); + String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator); // Encode the class loader contexts for the dexPathsToRegister. String[] classLoaderContexts = DexoptUtils.processContextForDexLoad( diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java index e1310a2f1ab3..d2600b5060b5 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java +++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java @@ -318,7 +318,8 @@ public final class DexoptUtils { // is fine (they come over binder). Even if something changes we expect the sizes to be // very small and it shouldn't matter much. for (int i = 1; i < classLoadersNames.size(); i++) { - if (!ClassLoaderFactory.isValidClassLoaderName(classLoadersNames.get(i))) { + if (!ClassLoaderFactory.isValidClassLoaderName(classLoadersNames.get(i)) + || classPaths.get(i) == null) { return null; } String classpath = encodeClasspath(classPaths.get(i).split(File.pathSeparator)); diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 2be55c02638d..70c86a1672fa 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -1856,7 +1856,10 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public void notifyThrottling(Temperature temp) { boolean isThrottling = temp.getStatus() >= Temperature.THROTTLING_SEVERE; StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(), - isThrottling ? 1 : 0, temp.getValue()); + isThrottling ? + StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__START : + StatsLog.THERMAL_THROTTLING_STATE_CHANGED__STATE__STOP, + temp.getValue()); } } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 6034f81f9cc8..7b7598e9b39d 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1280,15 +1280,24 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (!aInfo.processName.equals("system")) { if ((startFlags & (START_FLAG_DEBUG | START_FLAG_NATIVE_DEBUGGING | START_FLAG_TRACK_ALLOCATION)) != 0 || profilerInfo != null) { - /** - * Assume safe to call into AMS synchronously because the call that set these - * flags should have originated from AMS which will already have its lock held. - * @see ActivityManagerService#startActivityAndWait(IApplicationThread, String, - * Intent, String, IBinder, String, int, int, ProfilerInfo, Bundle, int) - * TODO(b/80414790): Investigate a better way of untangling this. - */ - mService.mAmInternal.setDebugFlagsForStartingActivity( - aInfo, startFlags, profilerInfo); + + // Mimic an AMS synchronous call by passing a message to AMS and wait for AMS + // to notify us that the task has completed. + // TODO(b/80414790) look into further untangling for the situation where the + // caller is on the same thread as the handler we are posting to. + synchronized (mService.mGlobalLock) { + // Post message to AMS. + final Message msg = PooledLambda.obtainMessage( + ActivityManagerInternal::setDebugFlagsForStartingActivity, + mService.mAmInternal, aInfo, startFlags, profilerInfo, + mService.mGlobalLock); + mService.mH.sendMessage(msg); + try { + mService.mGlobalLock.wait(); + } catch (InterruptedException ignore) { + + } + } } } final String intentLaunchToken = intent.getLaunchToken(); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 15a3a1a3c378..04a526fe4cf2 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -98,7 +98,7 @@ cc_defaults { "libhwbinder", "libutils", "libhwui", - "libbpf", + "libbpf_android", "libnetdbpf", "libnetdutils", "android.hardware.audio.common@2.0", diff --git a/services/tests/servicestests/src/com/android/server/job/JobSetTest.java b/services/tests/servicestests/src/com/android/server/job/JobSetTest.java index e62e07d6858f..6b7634db8a60 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobSetTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobSetTest.java @@ -29,6 +29,7 @@ import android.content.Context; import android.content.pm.PackageManagerInternal; import android.os.Build; import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -47,6 +48,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest +@Presubmit public class JobSetTest { private static final String TAG = JobSetTest.class.getSimpleName(); private static final int SECONDARY_USER_ID_1 = 12; @@ -64,6 +66,7 @@ public class JobSetTest { final PackageManagerInternal pm = mock(PackageManagerInternal.class); when(pm.getPackageTargetSdkVersion(anyString())) .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT); + LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, pm); assumeFalse("Test cannot run in user " + mContext.getUserId(), mContext.getUserId() == SECONDARY_USER_ID_1 diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java index 553d234adfca..c6be1c022283 100644 --- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java @@ -16,6 +16,10 @@ package com.android.server.pm; +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_IGNORED; +import static android.app.AppOpsManager.OP_PLAY_AUDIO; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -35,12 +39,14 @@ import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.SuspendDialogInfo; import android.content.res.Resources; +import android.media.AudioAttributes; import android.os.BaseBundle; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; @@ -54,6 +60,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity; import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver; @@ -550,6 +558,32 @@ public class SuspendPackagesTest { assertEquals(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction()); } + @Test + public void testAudioOpBlockedOnSuspend() throws Exception { + final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + final CountDownLatch latch = new CountDownLatch(1); + final IAppOpsCallback watcher = new IAppOpsCallback.Stub() { + @Override + public void opChanged(int op, int uid, String packageName) { + if (op == OP_PLAY_AUDIO && packageName.equals(TEST_APP_PACKAGE_NAME)) { + latch.countDown(); + } + } + }; + iAppOps.startWatchingMode(OP_PLAY_AUDIO, TEST_APP_PACKAGE_NAME, watcher); + final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0); + int audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO, + AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME); + assertEquals("Audio muted for unsuspended package", MODE_ALLOWED, audioOpMode); + suspendTestPackage(null, null, null); + assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS)); + audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO, + AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME); + assertEquals("Audio not muted for suspended package", MODE_IGNORED, audioOpMode); + iAppOps.stopWatchingMode(watcher); + } + @After public void tearDown() throws IOException { mAppCommsReceiver.unregister(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index bd42b7318158..dad7b93e822e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -68,6 +68,7 @@ public class DexManagerTests { private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName(); private static final String DELEGATE_LAST_CLASS_LOADER_NAME = DelegateLastClassLoader.class.getName(); + private static final String UNSUPPORTED_CLASS_LOADER_NAME = "unsupported.class_loader"; @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); @Mock Installer mInstaller; @@ -105,7 +106,7 @@ public class DexManagerTests { mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1); mBarUser0UnsupportedClassLoader = new TestData(bar, isa, mUser0, - "unsupported.class_loader"); + UNSUPPORTED_CLASS_LOADER_NAME); mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0, DELEGATE_LAST_CLASS_LOADER_NAME); @@ -405,6 +406,24 @@ public class DexManagerTests { } @Test + public void testNotifySupportedAndUnsupportedClassLoader() { + String classPath = String.join(File.pathSeparator, mBarUser0.getSecondaryDexPaths()); + List<String> classLoaders = + Arrays.asList(PATH_CLASS_LOADER_NAME, UNSUPPORTED_CLASS_LOADER_NAME); + List<String> classPaths = Arrays.asList(classPath, classPath); + notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0); + + assertNoUseInfo(mBarUser0); + } + + @Test + public void testNotifyNullClassPath() { + notifyDexLoad(mBarUser0, null, mUser0); + + assertNoUseInfo(mBarUser0); + } + + @Test public void testNotifyVariableClassLoader() { // Record bar secondaries with the default PathClassLoader. List<String> secondaries = mBarUser0.getSecondaryDexPaths(); @@ -499,14 +518,17 @@ public class DexManagerTests { // By default, assume a single class loader in the chain. // This makes writing tests much easier. List<String> classLoaders = Arrays.asList(testData.mClassLoader); - List<String> classPaths = Arrays.asList(String.join(File.pathSeparator, dexPaths)); + List<String> classPaths = (dexPaths == null) + ? Arrays.asList((String) null) + : Arrays.asList(String.join(File.pathSeparator, dexPaths)); notifyDexLoad(testData, classLoaders, classPaths, loaderUserId); } - private void notifyDexLoad(TestData testData, List<String> classLoader, List<String> classPaths, - int loaderUserId) { - mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, classLoader, classPaths, - testData.mLoaderIsa, loaderUserId); + private void notifyDexLoad(TestData testData, List<String> classLoaders, + List<String> classPaths, int loaderUserId) { + // We call the internal function so any exceptions thrown cause test failures. + mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders, + classPaths, testData.mLoaderIsa, loaderUserId); } private PackageUseInfo getPackageUseInfo(TestData testData) { diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java index 77f517b7c48b..cd15a57368e8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java @@ -353,6 +353,18 @@ public class DexoptUtilsTest { } @Test + public void testProcessContextForDexLoadNoClassPath() { + List<String> classLoaders = Arrays.asList( + DELEGATE_LAST_CLASS_LOADER_NAME, + PATH_CLASS_LOADER_NAME); + List<String> classPaths = Arrays.asList( + String.join(File.pathSeparator, "foo.dex", "bar.dex"), + null); + String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths); + assertNull(context); + } + + @Test public void testProcessContextForDexLoadIllegalCallEmptyList() { boolean gotException = false; try { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 0d7b5843f47e..bcba15df8756 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -114,6 +114,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { Bundle smartActions = new Bundle(); Bundle smartReplies = new Bundle(); Bundle audiblyAlerted = new Bundle(); + Bundle noisy = new Bundle(); for (int i = 0; i < mKeys.length; i++) { String key = mKeys[i]; @@ -134,12 +135,13 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { smartActions.putParcelableArrayList(key, getSmartActions(key, i)); smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i)); audiblyAlerted.putBoolean(key, audiblyAlerted(i)); + noisy.putBoolean(key, getNoisy(i)); } NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys, interceptedKeys.toArray(new String[0]), visibilityOverrides, suppressedVisualEffects, importance, explanation, overrideGroupKeys, channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden, - smartActions, smartReplies, audiblyAlerted); + smartActions, smartReplies, audiblyAlerted, noisy); return update; } @@ -195,6 +197,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return index < 2; } + private boolean getNoisy(int index) { + return index < 1; + } + private ArrayList<String> getPeople(String key, int index) { ArrayList<String> people = new ArrayList<>(); for (int i = 0; i < index; i++) { diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp index c39688cb8f2d..0c40a6b5fa84 100644 --- a/startop/view_compiler/Android.bp +++ b/startop/view_compiler/Android.bp @@ -62,3 +62,22 @@ cc_test_host { ], test_suites: ["general-tests"], } + +cc_binary_host { + name: "dex_testcase_generator", + defaults: ["viewcompiler_defaults"], + srcs: ["dex_testcase_generator.cc"], + static_libs: [ + "libviewcompiler", + ], +} + +genrule { + name: "generate_dex_testcases", + tools: [":dex_testcase_generator"], + cmd: "$(location :dex_testcase_generator) $(genDir)", + out: [ + "simple.dex", + "trivial.dex", + ], +} diff --git a/startop/view_compiler/README.md b/startop/view_compiler/README.md index 56595016cbb9..f8da02b53907 100644 --- a/startop/view_compiler/README.md +++ b/startop/view_compiler/README.md @@ -23,3 +23,31 @@ This tool is still in its early stages and has a number of limitations. application. * This only works for apps that do not use a custom layout inflater. * Other limitations yet to be discovered. + +## DexBuilder Tests + +The DexBuilder has several low-level end to end tests to verify generated DEX +code validates, runs, and has the correct behavior. There are, unfortunately, a +number of pieces that must be added to generate new tests. Here are the +components: + +* `dex_testcase_generator` - Written in C++ using `DexBuilder`. This runs as a + build step produce the DEX files that will be tested on device. See the + `genrule` named `generate_dex_testcases` in `Android.bp`. These files are then + copied over to the device by TradeFed when running tests. +* `DexBuilderTest` - This is a Java Language test harness that loads the + generated DEX files and exercises methods in the file. + +To add a new DEX file test, follow these steps: +1. Modify `dex_testcase_generator` to produce the DEX file. +2. Add the filename to the `out` list of the `generate_dex_testcases` rule in + `Android.bp`. +3. Add a new `push` option to `AndroidTest.xml` to copy the DEX file to the + device. +4. Modify `DexBuilderTest.java` to load and exercise the new test. + +In each case, you should be able to cargo-cult the existing test cases. + +In general, you can probably get by without adding a new generated DEX file, and +instead add more methods to the files that are already generated. In this case, +you can skip all of steps 2 and 3 above, and simplify steps 1 and 4. diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING new file mode 100644 index 000000000000..5d675b76b395 --- /dev/null +++ b/startop/view_compiler/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "dex-builder-test" + } + ] +} diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp new file mode 100644 index 000000000000..4449ea0f707e --- /dev/null +++ b/startop/view_compiler/dex_builder_test/Android.bp @@ -0,0 +1,29 @@ +// +// 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. +// + +android_test { + name: "dex-builder-test", + srcs: ["src/android/startop/test/DexBuilderTest.java"], + sdk_version: "current", + data: [":generate_dex_testcases"], + static_libs: [ + "android-support-test", + "guava", + ], + manifest: "AndroidManifest.xml", + test_config: "AndroidTest.xml", + test_suites: ["general-tests"], +} diff --git a/startop/view_compiler/dex_builder_test/AndroidManifest.xml b/startop/view_compiler/dex_builder_test/AndroidManifest.xml new file mode 100644 index 000000000000..6ac5fc5db345 --- /dev/null +++ b/startop/view_compiler/dex_builder_test/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?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="android.startop.test" > + + <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="android.startop.test" + android:label="DexBuilder Tests"/> + +</manifest> diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml new file mode 100644 index 000000000000..6f90cf3b81a7 --- /dev/null +++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml @@ -0,0 +1,34 @@ +<?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. +--> +<configuration description="Runs DexBuilder Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="dex-builder-test.apk" /> + </target_preparer> + + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" /> + <option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="android.startop.test" /> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java new file mode 100644 index 000000000000..87b25780aaa8 --- /dev/null +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java @@ -0,0 +1,68 @@ +/* + * 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 android.startop.test; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import com.google.common.io.ByteStreams; +import dalvik.system.InMemoryDexClassLoader; +import dalvik.system.PathClassLoader; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import org.junit.Assert; +import org.junit.Test; + +// Adding tests here requires changes in several other places. See README.md in +// the view_compiler directory for more information. +public class DexBuilderTest { + static ClassLoader loadDexFile(String filename) throws Exception { + return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename, + ClassLoader.getSystemClassLoader()); + } + + public void hello() {} + + @Test + public void loadTrivialDex() throws Exception { + ClassLoader loader = loadDexFile("trivial.dex"); + loader.loadClass("android.startop.test.testcases.Trivial"); + } + + @Test + public void return5() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("return5"); + Assert.assertEquals(5, method.invoke(null)); + } + + @Test + public void returnParam() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("returnParam", int.class); + Assert.assertEquals(5, method.invoke(null, 5)); + Assert.assertEquals(42, method.invoke(null, 42)); + } + + @Test + public void returnStringLength() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("returnStringLength", String.class); + Assert.assertEquals(13, method.invoke(null, "Hello, World!")); + } +} diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc new file mode 100644 index 000000000000..898817b4768c --- /dev/null +++ b/startop/view_compiler/dex_testcase_generator.cc @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#include "android-base/logging.h" +#include "dex_builder.h" + +#include <fstream> +#include <string> + +// Adding tests here requires changes in several other places. See README.md in +// the view_compiler directory for more information. + +using namespace startop::dex; +using namespace std; + +void GenerateTrivialDexFile(const string& outdir) { + DexBuilder dex_file; + + ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.Trivial")}; + cbuilder.set_source_file("dex_testcase_generator.cc#GenerateTrivialDexFile"); + + slicer::MemView image{dex_file.CreateImage()}; + std::ofstream out_file(outdir + "/trivial.dex"); + out_file.write(image.ptr<const char>(), image.size()); +} + +// Generates test cases that test around 1 instruction. +void GenerateSimpleTestCases(const string& outdir) { + DexBuilder dex_file; + + ClassBuilder cbuilder{dex_file.MakeClass("android.startop.test.testcases.SimpleTests")}; + cbuilder.set_source_file("dex_testcase_generator.cc#GenerateSimpleTestCases"); + + // int return5() { return 5; } + auto return5{cbuilder.CreateMethod("return5", Prototype{TypeDescriptor::Int()})}; + Value r{return5.MakeRegister()}; + return5.BuildConst4(r, 5); + return5.BuildReturn(r); + return5.Encode(); + + // // int returnParam(int x) { return x; } + auto returnParam{cbuilder.CreateMethod("returnParam", + Prototype{TypeDescriptor::Int(), TypeDescriptor::Int()})}; + returnParam.BuildReturn(Value::Parameter(0)); + returnParam.Encode(); + + // int returnStringLength(String x) { return x.length(); } + auto string_type{TypeDescriptor::FromClassname("java.lang.String")}; + MethodDeclData string_length{ + dex_file.GetOrDeclareMethod(string_type, "length", Prototype{TypeDescriptor::Int()})}; + + auto returnStringLength{ + cbuilder.CreateMethod("returnStringLength", Prototype{TypeDescriptor::Int(), string_type})}; + Value result = returnStringLength.MakeRegister(); + returnStringLength.AddInstruction( + Instruction::InvokeVirtual(string_length.id, result, Value::Parameter(0))); + returnStringLength.BuildReturn(result); + returnStringLength.Encode(); + + slicer::MemView image{dex_file.CreateImage()}; + std::ofstream out_file(outdir + "/simple.dex"); + out_file.write(image.ptr<const char>(), image.size()); +} + +int main(int argc, char** argv) { + CHECK_EQ(argc, 2); + + string outdir = argv[1]; + + GenerateTrivialDexFile(outdir); + GenerateSimpleTestCases(outdir); +} diff --git a/telecomm/java/android/telecom/Logging/EventManager.java b/telecomm/java/android/telecom/Logging/EventManager.java index 2bda6480b99e..1342038c6477 100644 --- a/telecomm/java/android/telecom/Logging/EventManager.java +++ b/telecomm/java/android/telecom/Logging/EventManager.java @@ -180,7 +180,7 @@ public class EventManager { } } - private final List<Event> mEvents = new LinkedList<>(); + private final List<Event> mEvents = Collections.synchronizedList(new LinkedList<>()); private final Loggable mRecordEntry; public EventRecord(Loggable recordEntry) { @@ -197,7 +197,7 @@ public class EventManager { } public List<Event> getEvents() { - return mEvents; + return new LinkedList<>(mEvents); } public List<EventTiming> extractEventTimings() { @@ -207,21 +207,24 @@ public class EventManager { LinkedList<EventTiming> result = new LinkedList<>(); Map<String, PendingResponse> pendingResponses = new HashMap<>(); - for (Event event : mEvents) { - if (requestResponsePairs.containsKey(event.eventId)) { - // This event expects a response, so add that expected response to the maps - // of pending events. - for (EventManager.TimedEventPair p : requestResponsePairs.get(event.eventId)) { - pendingResponses.put(p.mResponse, new PendingResponse(event.eventId, - event.time, p.mTimeoutMillis, p.mName)); + synchronized (mEvents) { + for (Event event : mEvents) { + if (requestResponsePairs.containsKey(event.eventId)) { + // This event expects a response, so add that expected response to the maps + // of pending events. + for (EventManager.TimedEventPair p : requestResponsePairs.get( + event.eventId)) { + pendingResponses.put(p.mResponse, new PendingResponse(event.eventId, + event.time, p.mTimeoutMillis, p.mName)); + } } - } - PendingResponse pendingResponse = pendingResponses.remove(event.eventId); - if (pendingResponse != null) { - long elapsedTime = event.time - pendingResponse.requestEventTimeMillis; - if (elapsedTime < pendingResponse.timeoutMillis) { - result.add(new EventTiming(pendingResponse.name, elapsedTime)); + PendingResponse pendingResponse = pendingResponses.remove(event.eventId); + if (pendingResponse != null) { + long elapsedTime = event.time - pendingResponse.requestEventTimeMillis; + if (elapsedTime < pendingResponse.timeoutMillis) { + result.add(new EventTiming(pendingResponse.name, elapsedTime)); + } } } } @@ -233,7 +236,8 @@ public class EventManager { pw.print(mRecordEntry.getDescription()); pw.increaseIndent(); - for (Event event : mEvents) { + // Iterate over copy of events so that this doesn't hold the lock for too long. + for (Event event : getEvents()) { pw.print(event.timestampString); pw.print(" - "); pw.print(event.eventId); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 3f9a5336e611..fbc54ae6a857 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1079,24 +1079,44 @@ public class CarrierConfigManager { "wfc_operator_error_codes_string_array"; /** - * Indexes of SPN format strings in wfcSpnFormats and wfcDataSpnFormats. + * Indexes of SPN format strings in wfcSpnFormats. * * <p>Available options are: * <ul> - * <li> 0: %s</li> - * <li> 1: %s Wi-Fi Calling</li> - * <li> 2: WLAN Call</li> - * <li> 3: %s WLAN Call</li> - * <li> 4: %s Wi-Fi</li> - * <li> 5: WiFi Calling | %s</li> - * <li> 6: %s VoWifi</li> + * <li> 0: %s</li> + * <li> 1: %s Wi-Fi Calling</li> + * <li> 2: WLAN Call</li> + * <li> 3: %s WLAN Call</li> + * <li> 4: %s Wi-Fi</li> + * <li> 5: WiFi Calling | %s</li> + * <li> 6: %s VoWifi</li> + * <li> 7: Wi-Fi Calling</li> + * <li> 8: Wi-Fi</li> + * <li> 9: WiFi Calling</li> + * <li> 10: VoWifi</li> * @hide */ public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int"; - /** @hide */ + + /** + * Indexes of data SPN format strings in wfcSpnFormats. + * + * @see KEY_WFC_SPN_FORMAT_IDX_INT for available options. + * @hide + */ public static final String KEY_WFC_DATA_SPN_FORMAT_IDX_INT = "wfc_data_spn_format_idx_int"; /** + * Indexes of SPN format strings in wfcSpnFormats used during flight mode. + * + * Set to -1 to use the value from KEY_WFC_SPN_FORMAT_IDX_INT also in this case. + * @see KEY_WFC_SPN_FORMAT_IDX_INT for other available options. + * @hide + */ + public static final String KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT = + "wfc_flight_mode_spn_format_idx_int"; + + /** * Use root locale when reading wfcSpnFormats. * * If true, then the root locale will always be used when reading wfcSpnFormats. This means the @@ -2456,6 +2476,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY, null); sDefaults.putInt(KEY_WFC_SPN_FORMAT_IDX_INT, 0); sDefaults.putInt(KEY_WFC_DATA_SPN_FORMAT_IDX_INT, 0); + sDefaults.putInt(KEY_WFC_FLIGHT_MODE_SPN_FORMAT_IDX_INT, -1); sDefaults.putBoolean(KEY_WFC_SPN_USE_ROOT_LOCALE, false); sDefaults.putString(KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING, ""); sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false); diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index f12756a8062f..af7123b84842 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -30,7 +30,9 @@ import static android.os.Process.SYSTEM_UID; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; @@ -63,24 +65,13 @@ public class PermissionMonitorTest { @Mock private PackageManager mPackageManager; private PermissionMonitor mPermissionMonitor; - private int mMockFirstSdkInt; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES); - // Try to use spy() here for stubbing getDeviceFirstSdkInt value but the spies are loaded - // by a custom class loader that's different from the loader used for loading the real - // thing. That means those two classes are not in the same package, so a package private - // method is not accessible. Hence, using override method to control FIRST_SDK_INT value - // instead of spy function for testing. - mPermissionMonitor = new PermissionMonitor(mContext, null) { - @Override - int getDeviceFirstSdkInt() { - return mMockFirstSdkInt; - } - }; + mPermissionMonitor = spy(new PermissionMonitor(mContext, null)); } private boolean hasBgPermission(String partition, int targetSdkVersion, int uid, @@ -166,13 +157,13 @@ public class PermissionMonitorTest { @Test public void testHasUseBackgroundNetworksPermissionSystemUid() throws Exception { - mMockFirstSdkInt = VERSION_P; + doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt(); assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID)); assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CHANGE_WIFI_STATE)); assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - mMockFirstSdkInt = VERSION_Q; + doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt(); assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID)); assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CHANGE_WIFI_STATE)); assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, |