diff options
43 files changed, 544 insertions, 319 deletions
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index b0b1874107ce..23fbefb73c50 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -17,7 +17,6 @@ package android.content.pm; import android.Manifest; -import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -177,8 +176,7 @@ public abstract class RegisteredServicesCache<V> { mContext.registerReceiver(mUserRemovedReceiver, userFilter); } - @VisibleForTesting - protected void handlePackageEvent(Intent intent, int userId) { + private void handlePackageEvent(Intent intent, int userId) { // Don't regenerate the services map when the package is removed or its // ASEC container unmounted as a step in replacement. The subsequent // _ADDED / _AVAILABLE call will regenerate the map in the final state. @@ -240,9 +238,6 @@ public abstract class RegisteredServicesCache<V> { public void invalidateCache(int userId) { synchronized (mServicesLock) { - if (DEBUG) { - Slog.d(TAG, "invalidating cache for " + userId + " " + mInterfaceName); - } final UserServices<V> user = findOrCreateUserLocked(userId); user.services = null; onServicesChangedLocked(userId); @@ -472,48 +467,34 @@ public abstract class RegisteredServicesCache<V> { * or null to assume that everything is affected. * @param userId the user for whom to update the services map. */ - private void generateServicesMap(@Nullable int[] changedUids, int userId) { + private void generateServicesMap(int[] changedUids, int userId) { if (DEBUG) { Slog.d(TAG, "generateServicesMap() for " + userId + ", changed UIDs = " + Arrays.toString(changedUids)); } + final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>(); + final List<ResolveInfo> resolveInfos = queryIntentServices(userId); + for (ResolveInfo resolveInfo : resolveInfos) { + try { + ServiceInfo<V> info = parseServiceInfo(resolveInfo); + if (info == null) { + Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); + continue; + } + serviceInfos.add(info); + } catch (XmlPullParserException | IOException e) { + Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); + } + } + synchronized (mServicesLock) { final UserServices<V> user = findOrCreateUserLocked(userId); - final boolean cacheInvalid = user.services == null; - if (cacheInvalid) { + final boolean firstScan = user.services == null; + if (firstScan) { user.services = Maps.newHashMap(); } - final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<>(); - final List<ResolveInfo> resolveInfos = queryIntentServices(userId); - - for (ResolveInfo resolveInfo : resolveInfos) { - try { - // when changedUids == null, we want to do a rescan of everything, this means - // it's the initial scan, and containsUid will trivially return true - // when changedUids != null, we got here because a package changed, but - // invalidateCache could have been called (thus user.services == null), and we - // should query from PackageManager again - if (!cacheInvalid - && !containsUid( - changedUids, resolveInfo.serviceInfo.applicationInfo.uid)) { - if (DEBUG) { - Slog.d(TAG, "Skipping parseServiceInfo for " + resolveInfo); - } - continue; - } - ServiceInfo<V> info = parseServiceInfo(resolveInfo); - if (info == null) { - Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); - continue; - } - serviceInfos.add(info); - } catch (XmlPullParserException | IOException e) { - Log.w(TAG, "Unable to load service info " + resolveInfo.toString(), e); - } - } - StringBuilder changes = new StringBuilder(); boolean changed = false; for (ServiceInfo<V> info : serviceInfos) { @@ -534,7 +515,7 @@ public abstract class RegisteredServicesCache<V> { changed = true; user.services.put(info.type, info); user.persistentServices.put(info.type, info.uid); - if (!(user.mPersistentServicesFileDidNotExist && cacheInvalid)) { + if (!(user.mPersistentServicesFileDidNotExist && firstScan)) { notifyListener(info.type, userId, false /* removed */); } } else if (previousUid == info.uid) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6f0669b15dad..bf8b0066cb64 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7764,6 +7764,12 @@ public final class Settings { */ public static final String SKIP_GESTURE_COUNT = "skip_gesture_count"; + /** + * Count of non-gesture interaction. + * @hide + */ + public static final String SKIP_TOUCH_COUNT = "skip_touch_count"; + private static final Validator SKIP_GESTURE_COUNT_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; @@ -7808,11 +7814,22 @@ public final class Settings { public static final String SILENCE_CALL_GESTURE_COUNT = "silence_call_gesture_count"; /** - * Count of successful silence notification gestures. + * Count of non-gesture interaction. + * @hide + */ + public static final String SILENCE_ALARMS_TOUCH_COUNT = "silence_alarms_touch_count"; + + /** + * Count of non-gesture interaction. + * @hide + */ + public static final String SILENCE_TIMER_TOUCH_COUNT = "silence_timer_touch_count"; + + /** + * Count of non-gesture interaction. * @hide */ - public static final String SILENCE_NOTIFICATION_GESTURE_COUNT = - "silence_notification_gesture_count"; + public static final String SILENCE_CALL_TOUCH_COUNT = "silence_call_touch_count"; private static final Validator SILENCE_GESTURE_COUNT_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR; @@ -9036,10 +9053,13 @@ public final class Settings { NAVIGATION_MODE, AWARE_ENABLED, SKIP_GESTURE_COUNT, + SKIP_TOUCH_COUNT, SILENCE_ALARMS_GESTURE_COUNT, - SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_CALL_GESTURE_COUNT, SILENCE_TIMER_GESTURE_COUNT, + SILENCE_ALARMS_TOUCH_COUNT, + SILENCE_CALL_TOUCH_COUNT, + SILENCE_TIMER_TOUCH_COUNT, DARK_MODE_DIALOG_SEEN, GLOBAL_ACTIONS_PANEL_ENABLED, AWARE_LOCK_ENABLED @@ -9227,10 +9247,13 @@ public final class Settings { VALIDATORS.put(NAVIGATION_MODE, NAVIGATION_MODE_VALIDATOR); VALIDATORS.put(AWARE_ENABLED, AWARE_ENABLED_VALIDATOR); VALIDATORS.put(SKIP_GESTURE_COUNT, SKIP_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SKIP_TOUCH_COUNT, SKIP_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_ALARMS_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_TIMER_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); - VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SILENCE_ALARMS_TOUCH_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SILENCE_TIMER_TOUCH_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); + VALIDATORS.put(SILENCE_CALL_TOUCH_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR); VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR); VALIDATORS.put(DARK_MODE_DIALOG_SEEN, BOOLEAN_VALIDATOR); VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index f9d27bb46592..00206fc38d1d 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1534,7 +1534,11 @@ public class ChooserActivity extends ResolverActivity { if (driList.get(i).getResolvedComponentName().equals( resultList.get(j).getTargetComponent())) { ShortcutManager.ShareShortcutInfo shareShortcutInfo = resultList.get(j); - ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo); + // Incoming results are ordered but without a score. Create a score + // based on the index in order to be sorted appropriately when joined + // with legacy direct share api results. + float score = Math.max(1.0f - (0.05f * j), 0.0f); + ChooserTarget chooserTarget = convertToChooserTarget(shareShortcutInfo, score); chooserTargets.add(chooserTarget); if (mDirectShareAppTargetCache != null && appTargets != null) { mDirectShareAppTargetCache.put(chooserTarget, appTargets.get(j)); @@ -1580,7 +1584,8 @@ public class ChooserActivity extends ResolverActivity { return false; } - private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut) { + private ChooserTarget convertToChooserTarget(ShortcutManager.ShareShortcutInfo shareShortcut, + float score) { ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo(); Bundle extras = new Bundle(); extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId()); @@ -1591,7 +1596,7 @@ public class ChooserActivity extends ResolverActivity { null, // The ranking score for this target (0.0-1.0); the system will omit items with low // scores when there are too many Direct Share items. - 1.0f, + score, // The name of the component to be launched if this target is chosen. shareShortcut.getTargetComponent().clone(), // The extra values here will be merged into the Intent when this target is chosen. diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 6e97076fefdf..55e21a4774d9 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -146,14 +146,6 @@ public final class SystemUiDeviceConfigFlags { public static final String ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS = "assist_handles_shown_frequency_threshold_ms"; - // Flag related to clock face - - /** - * (String) Contains the clock plugin service names that are not allow to be shown. - * Each service name is seperated by a comma(",") in the string. - */ - public static final String CLOCK_FACE_BLACKLIST = "clock_face_blacklist"; - /** * (long) How long, in milliseconds, for teaching behaviors to wait before considering the user * taught. diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 7ec8309e47de..d28c72f319af 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -188,6 +188,13 @@ public class ZygoteInit { System.loadLibrary("android"); System.loadLibrary("compiler_rt"); System.loadLibrary("jnigraphics"); + + // tolerate missing sfplugin_ccodec which is only present on Codec 2 devices + try { + System.loadLibrary("sfplugin_ccodec"); + } catch (Error | RuntimeException e) { + Log.w(TAG, "Problem preloading sfplugin_ccodec: " + e); + } } native private static void nativePreloadAppProcessHALs(); diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 6f4f3374ac73..fe66cf9aab7d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1311,7 +1311,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return semiTransparentBarColor; } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { return Color.BLACK; - } else if (scrimTransparent && barColor == Color.TRANSPARENT) { + } else if (scrimTransparent && Color.alpha(barColor) == 0) { boolean light = (sysuiVis & lightSysuiFlag) != 0; return light ? SCRIM_LIGHT : semiTransparentBarColor; } else { diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index fed2efaf8a78..61799eefdca6 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -198,11 +198,19 @@ message SecureSettingsProto { optional SettingProto silence_alarms_count = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto silence_calls_count = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto silence_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; - optional SettingProto silence_notification_count = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // del: silence_notification_count = 5 optional SettingProto silence_timer_count = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto skip_count = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto skip_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + optional SettingProto silence_alarms_touch_count = 9 [ (android.privacy).dest = + DEST_AUTOMATIC ]; + optional SettingProto silence_calls_touch_count = 10 [ (android.privacy).dest = + DEST_AUTOMATIC ]; + optional SettingProto silence_timer_touch_count = 11 [ (android.privacy).dest = + DEST_AUTOMATIC ]; + optional SettingProto skip_touch_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Gesture gesture = 74; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 600330c356ed..305c1a6d5657 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3787,9 +3787,11 @@ <integer name="config_stableDeviceDisplayWidth">-1</integer> <integer name="config_stableDeviceDisplayHeight">-1</integer> - <!-- Decide whether to display 'No service' on status bar instead of 'Emergency calls only' - when SIM is unready. --> - <bool name="config_display_no_service_when_sim_unready">false</bool> + <!-- List of countries in which we display 'No service' on status bar + instead of 'Emergency calls only' when SIM is unready. --> + <string-array translatable="false" name="config_display_no_service_when_sim_unready"> + <item>"DE"</item> + </string-array> <!-- Class names of device specific services inheriting com.android.server.SystemService. The classes are instantiated in the order of the array. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 51cd7441ef26..6e03f5e8e018 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3552,7 +3552,7 @@ <java-symbol type="integer" name="config_stableDeviceDisplayWidth" /> <java-symbol type="integer" name="config_stableDeviceDisplayHeight" /> - <java-symbol type="bool" name="config_display_no_service_when_sim_unready" /> + <java-symbol type="array" name="config_display_no_service_when_sim_unready" /> <java-symbol type="layout" name="slice_grid" /> <java-symbol type="layout" name="slice_message_local" /> diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index c8150b12a23b..365e97ded928 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.content.Intent; import android.content.res.Resources; import android.os.FileUtils; import android.os.Parcel; @@ -190,36 +189,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { assertEquals(0, cache.getPersistentServicesSize(u1)); } - /** - * Check that an optimization to skip a call to PackageManager handles an invalidated cache. - * - * We added an optimization in generateServicesMap to only query PackageManager for packages - * that have been changed, because if a package is unchanged, we have already cached the - * services info for it, so we can save a query to PackageManager (and save some memory). - * However, if invalidateCache was called, we cannot optimize, and must do a full query. - * The initial optimization was buggy because it failed to check for an invalidated cache, and - * only scanned the changed packages, given in the ACTION_PACKAGE_CHANGED intent (b/122912184). - */ - public void testParseServiceInfoOptimizationHandlesInvalidatedCache() { - TestServicesCache cache = new TestServicesCache(); - cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1)); - cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2)); - assertEquals(2, cache.getAllServicesSize(U0)); - - // simulate the client of the cache invalidating it - cache.invalidateCache(U0); - - // there should be 0 services (userServices.services == null ) at this point, but we don't - // call getAllServicesSize since that would force a full scan of packages, - // instead we trigger a package change in a package that is in the list of services - Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED); - intent.putExtra(Intent.EXTRA_UID, UID1); - cache.handlePackageEvent(intent, U0); - - // check that the optimization does a full query and caches both services - assertEquals(2, cache.getAllServicesSize(U0)); - } - private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo( TestServiceType type, int uid) { final ComponentInfo info = new ComponentInfo(); @@ -297,11 +266,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { map = new HashMap<>(); mServices.put(userId, map); } - // in actual cases, resolveInfo should always have a serviceInfo, since we specifically - // query for intent services - resolveInfo.serviceInfo = new android.content.pm.ServiceInfo(); - resolveInfo.serviceInfo.applicationInfo = - new ApplicationInfo(serviceInfo.componentInfo.applicationInfo); map.put(resolveInfo, serviceInfo); } @@ -340,11 +304,6 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { public void onUserRemoved(int userId) { super.onUserRemoved(userId); } - - @Override - public void handlePackageEvent(Intent intent, int userId) { - super.handlePackageEvent(intent, userId); - } } static class TestSerializer implements XmlSerializerAndParser<TestServiceType> { diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp index df0dd7ce463d..f1ab1493012a 100644 --- a/libs/androidfw/PosixUtils.cpp +++ b/libs/androidfw/PosixUtils.cpp @@ -64,6 +64,9 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv) return nullptr; } + auto gid = getgid(); + auto uid = getuid(); + char const** argv0 = (char const**)malloc(sizeof(char*) * (argv.size() + 1)); for (size_t i = 0; i < argv.size(); i++) { argv0[i] = argv[i].c_str(); @@ -75,6 +78,16 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv) PLOG(ERROR) << "fork"; return nullptr; case 0: // child + if (setgid(gid) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(uid) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + close(stdout[0]); if (dup2(stdout[1], STDOUT_FILENO) == -1) { abort(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 3cd82dfca6b6..b2c10ec8ea7f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1944,9 +1944,6 @@ class SettingsProtoDumpUtil { Settings.Secure.SILENCE_GESTURE, SecureSettingsProto.Gesture.SILENCE_ENABLED); dumpSetting(s, p, - Settings.Secure.SILENCE_NOTIFICATION_GESTURE_COUNT, - SecureSettingsProto.Gesture.SILENCE_NOTIFICATION_COUNT); - dumpSetting(s, p, Settings.Secure.SILENCE_TIMER_GESTURE_COUNT, SecureSettingsProto.Gesture.SILENCE_TIMER_COUNT); @@ -1956,6 +1953,19 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.SKIP_GESTURE, SecureSettingsProto.Gesture.SKIP_ENABLED); + + dumpSetting(s, p, + Settings.Secure.SILENCE_ALARMS_TOUCH_COUNT, + SecureSettingsProto.Gesture.SILENCE_ALARMS_TOUCH_COUNT); + dumpSetting(s, p, + Settings.Secure.SILENCE_CALL_TOUCH_COUNT, + SecureSettingsProto.Gesture.SILENCE_CALLS_TOUCH_COUNT); + dumpSetting(s, p, + Settings.Secure.SILENCE_TIMER_TOUCH_COUNT, + SecureSettingsProto.Gesture.SILENCE_TIMER_TOUCH_COUNT); + dumpSetting(s, p, + Settings.Secure.SKIP_TOUCH_COUNT, + SecureSettingsProto.Gesture.SKIP_TOUCH_COUNT); p.end(gestureToken); dumpSetting(s, p, diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 4df824e1cb06..a549870ec780 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -112,6 +112,9 @@ wifi,bt,dnd,flashlight,rotation,battery,cell,airplane,cast </string> + <!-- The minimum number of tiles to display in QuickSettings --> + <integer name="quick_settings_min_num_tiles">6</integer> + <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index f60613ae4a83..7feacb469f81 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2011,7 +2011,7 @@ <string name="drag_to_remove_tiles">Drag here to remove</string> <!-- Label to indicate to users that additional tiles cannot be removed. [CHAR LIMIT=60] --> - <string name="drag_to_remove_disabled">You need at least 6 tiles</string> + <string name="drag_to_remove_disabled">You need at least <xliff:g id="min_num_tiles" example="6">%1$d</xliff:g> tiles</string> <!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] --> <string name="qs_edit">Edit</string> diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 9edb54c146df..9e2464ea4fbb 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -15,8 +15,6 @@ */ package com.android.keyguard.clock; -import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLOCK_FACE_BLACKLIST; - import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; @@ -26,12 +24,9 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; -import android.provider.DeviceConfig; import android.provider.Settings; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.DisplayMetrics; -import android.util.Log; import android.view.LayoutInflater; import androidx.annotation.VisibleForTesting; @@ -47,12 +42,10 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; -import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; @@ -74,8 +67,6 @@ public final class ClockManager { private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final CurrentUserObservable mCurrentUserObservable; - private final ArraySet<String> mBlacklistedClockPlugins = new ArraySet<>(); - /** * Observe settings changes to know when to switch the clock face. */ @@ -164,41 +155,6 @@ public final class ClockManager { DisplayMetrics dm = res.getDisplayMetrics(); mWidth = dm.widthPixels; mHeight = dm.heightPixels; - - updateBlackList(); - registerDeviceConfigListener(); - } - - private void updateBlackList() { - String blacklist = getBlackListFromConfig(); - - mBlacklistedClockPlugins.clear(); - if (blacklist != null && !blacklist.isEmpty()) { - mBlacklistedClockPlugins.addAll(Arrays.asList(blacklist.split(","))); - } - } - - String getBlackListFromConfig() { - return DeviceConfig.getString( - DeviceConfig.NAMESPACE_SYSTEMUI, CLOCK_FACE_BLACKLIST, null); - } - - private void registerDeviceConfigListener() { - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_SYSTEMUI, - r -> mMainHandler.post(r), - properties -> onDeviceConfigPropertiesChanged(properties.getNamespace())); - } - - void onDeviceConfigPropertiesChanged(String namespace) { - if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { - Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: " - + namespace); - return; - } - - updateBlackList(); - reload(); } /** @@ -350,12 +306,10 @@ public final class ClockManager { } /** - * Get information about clock faces which are available and not in blacklist. + * Get information about available clock faces. */ List<ClockInfo> getInfo() { - return mClockInfo.stream() - .filter(info -> !mBlacklistedClockPlugins.contains(info.getId())) - .collect(Collectors.toList()); + return mClockInfo; } /** @@ -407,7 +361,7 @@ public final class ClockManager { if (ClockManager.this.isDocked()) { final String name = mSettingsWrapper.getDockedClockFace( mCurrentUserObservable.getCurrentUser().getValue()); - if (name != null && !mBlacklistedClockPlugins.contains(name)) { + if (name != null) { plugin = mClocks.get(name); if (plugin != null) { return plugin; @@ -416,7 +370,7 @@ public final class ClockManager { } final String name = mSettingsWrapper.getLockScreenCustomClockFace( mCurrentUserObservable.getCurrentUser().getValue()); - if (name != null && !mBlacklistedClockPlugins.contains(name)) { + if (name != null) { plugin = mClocks.get(name); } return plugin; diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 8ed5424cf673..2542abdbef72 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -19,6 +19,7 @@ import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -54,7 +55,6 @@ import java.util.ArrayList; import java.util.List; public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener { - private static final int MIN_NUM_TILES = 6; private static final long DRAG_LENGTH = 100; private static final float DRAG_SCALE = 1.2f; public static final long MOVE_DURATION = 150; @@ -79,6 +79,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private final ItemTouchHelper mItemTouchHelper; private final ItemDecoration mDecoration; private final AccessibilityManager mAccessibilityManager; + private final int mMinNumTiles; private int mEditIndex; private int mTileDividerIndex; private boolean mNeedsFocus; @@ -97,6 +98,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mItemTouchHelper = new ItemTouchHelper(mCallbacks); mDecoration = new TileItemDecoration(context); + mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles); } public void setHost(QSTileHost host) { @@ -247,15 +249,17 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta return; } if (holder.getItemViewType() == TYPE_EDIT) { - final int titleResId; + final String titleText; + Resources res = mContext.getResources(); if (mCurrentDrag == null) { - titleResId = R.string.drag_to_add_tiles; + titleText = res.getString(R.string.drag_to_add_tiles); } else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) { - titleResId = R.string.drag_to_remove_disabled; + titleText = res.getString(R.string.drag_to_remove_disabled, mMinNumTiles); } else { - titleResId = R.string.drag_to_remove_tiles; + titleText = res.getString(R.string.drag_to_remove_tiles); } - ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleResId); + + ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(titleText); return; } if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) { @@ -337,7 +341,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta } private boolean canRemoveTiles() { - return mCurrentSpecs.size() > MIN_NUM_TILES; + return mCurrentSpecs.size() > mMinNumTiles; } private void selectPosition(int position, View v) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 787cc971e6a1..aeb85748fd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -16,8 +16,11 @@ package com.android.systemui.statusbar; +import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; + import android.content.Context; import android.content.res.Resources; +import android.os.Handler; import android.os.Trace; import android.os.UserHandle; import android.util.Log; @@ -44,6 +47,7 @@ import java.util.List; import java.util.Stack; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; @@ -59,6 +63,8 @@ import dagger.Lazy; public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener { private static final String TAG = "NotificationViewHierarchyManager"; + private final Handler mHandler; + //TODO: change this top <Entry, List<Entry>>? private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap = new HashMap<>(); @@ -88,9 +94,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle // Used to help track down re-entrant calls to our update methods, which will cause bugs. private boolean mPerformingUpdate; + // Hack to get around re-entrant call in onDynamicPrivacyChanged() until we can track down + // the problem. + private boolean mIsHandleDynamicPrivacyChangeScheduled; @Inject public NotificationViewHierarchyManager(Context context, + @Named(MAIN_HANDLER_NAME) Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, @@ -100,6 +110,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle BubbleData bubbleData, KeyguardBypassController bypassController, DynamicPrivacyController privacyController) { + mHandler = mainHandler; mLockscreenUserManager = notificationLockscreenUserManager; mBypassController = bypassController; mGroupManager = groupManager; @@ -438,6 +449,20 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle @Override public void onDynamicPrivacyChanged() { + if (mPerformingUpdate) { + Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call"); + } + // This listener can be called from updateNotificationViews() via a convoluted listener + // chain, so we post here to prevent a re-entrant call. See b/136186188 + // TODO: Refactor away the need for this + if (!mIsHandleDynamicPrivacyChangeScheduled) { + mIsHandleDynamicPrivacyChangeScheduled = true; + mHandler.post(this::onHandleDynamicPrivacyChanged); + } + } + + private void onHandleDynamicPrivacyChanged() { + mIsHandleDynamicPrivacyChangeScheduled = false; updateNotificationViews(); } 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 58e639924f4f..9e3d74b138fa 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 @@ -4716,14 +4716,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return mIntrinsicPadding; } - /** - * @return the y position of the first notification - */ - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public float getNotificationsTopY() { - return mTopPadding + getStackTranslation(); - } - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public boolean shouldDelayChildPressedState() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index ea434fc5b344..49afae7415ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -95,6 +95,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private boolean mPulsing; private boolean mDozing; private boolean mDocked; + private boolean mBlockUpdates; private int mIconColor; private float mDozeAmount; private boolean mBouncerShowingScrimmed; @@ -107,8 +108,22 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange new KeyguardMonitor.Callback() { @Override public void onKeyguardShowingChanged() { + boolean force = false; + boolean wasShowing = mKeyguardShowing; mKeyguardShowing = mKeyguardMonitor.isShowing(); - update(); + if (!wasShowing && mKeyguardShowing && mBlockUpdates) { + mBlockUpdates = false; + force = true; + } + update(force); + } + + @Override + public void onKeyguardFadingAwayChanged() { + if (!mKeyguardMonitor.isKeyguardFadingAway() && mBlockUpdates) { + mBlockUpdates = false; + update(true /* force */); + } } }; private final DockManager.DockEventListener mDockEventListener = @@ -261,7 +276,11 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mIsFaceUnlockState = state == STATE_SCANNING_FACE; mLastState = state; - if (lastState != state || mForceUpdate) { + boolean shouldUpdate = lastState != state || mForceUpdate; + if (mBlockUpdates && canBlockUpdates()) { + shouldUpdate = false; + } + if (shouldUpdate) { mForceUpdate = false; @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState, state, mPulsing, mDozing); @@ -330,6 +349,10 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange return true; } + private boolean canBlockUpdates() { + return mKeyguardShowing || mKeyguardMonitor.isKeyguardFadingAway(); + } + private void updateClickability() { if (mAccessibilityController == null) { return; @@ -536,11 +559,17 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange /** * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the * icon on top of the black front scrim. + * @param wakeAndUnlock are we wake and unlocking + * @param isUnlock are we currently unlocking */ - public void onBiometricAuthModeChanged(boolean wakeAndUnlock) { + public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { if (wakeAndUnlock) { mWakeAndUnlockRunning = true; } + if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) { + // We don't want the icon to change while we are unlocking + mBlockUpdates = true; + } update(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 776cd4d71c94..22e3edb2bbd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -627,7 +627,7 @@ public class NavigationBarView extends FrameLayout implements if (mOverviewProxyService.isEnabled()) { // Force disable recents when not in legacy mode disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode); - if (pinningActive) { + if (pinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) { disableBack = disableHome = false; } } else if (pinningActive) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 53ce167542f0..6eeb96813c94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -932,7 +932,7 @@ public class NotificationPanelView extends PanelView implements protected void flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { mHeadsUpTouchHelper.notifyFling(!expand); - setClosingWithAlphaFadeout(!expand && getFadeoutAlpha() == 1.0f); + setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); } @@ -2068,8 +2068,11 @@ public class NotificationPanelView extends PanelView implements } private float getFadeoutAlpha() { - float alpha = (getNotificationsTopY() + mNotificationStackScroller.getFirstItemMinHeight()) - / mQsMinExpansionHeight; + float alpha; + if (mQsMinExpansionHeight == 0) { + return 1.0f; + } + alpha = getExpandedHeight() / mQsMinExpansionHeight; alpha = Math.max(0, Math.min(alpha, 1)); alpha = (float) Math.pow(alpha, 0.75); return alpha; @@ -2127,18 +2130,18 @@ public class NotificationPanelView extends PanelView implements float alpha; if (mBarState == StatusBarState.KEYGUARD) { - // When on Keyguard, we hide the header as soon as the top card of the notification - // stack scroller is close enough (collision distance) to the bottom of the header. - alpha = getNotificationsTopY() + // When on Keyguard, we hide the header as soon as we expanded close enough to the + // header + alpha = getExpandedHeight() / (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance); } else { // In SHADE_LOCKED, the top card is already really close to the header. Hide it as // soon as we start translating the stack. - alpha = getNotificationsTopY() / mKeyguardStatusBar.getHeight(); + alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight(); } - alpha = MathUtils.constrain(alpha, 0, 1); + alpha = MathUtils.saturate(alpha); alpha = (float) Math.pow(alpha, 0.75); return alpha; } @@ -2190,13 +2193,6 @@ public class NotificationPanelView extends PanelView implements mBigClockContainer.setAlpha(alpha); } - private float getNotificationsTopY() { - if (mNotificationStackScroller.getNotGoneChildCount() == 0) { - return getExpandedHeight(); - } - return mNotificationStackScroller.getNotificationsTopY(); - } - @Override protected void onExpandingStarted() { super.onExpandingStarted(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index e756d3a997f5..55f61fa8a6a0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3834,7 +3834,8 @@ public class StatusBar extends SystemUI implements DemoMode, public void notifyBiometricAuthModeChanged() { updateDozing(); updateScrimController(); - mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock()); + mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock(), + mBiometricUnlockController.isBiometricUnlock()); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 6dc2c8cab055..462b65f37ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -814,7 +814,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ public boolean shouldSubtleWindowAnimationsForUnlock() { return mStatusBar.mKeyguardBypassController.getBypassEnabled() - && mStatusBar.mState == StatusBarState.KEYGUARD; + && mStatusBar.mState == StatusBarState.KEYGUARD && !mBouncer.isAnimatingAway(); } public boolean isGoingToNotificationShade() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 667521bf6588..94054188d769 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -272,10 +272,11 @@ public class StatusBarWindowView extends FrameLayout { /** * Called when the biometric authentication mode changes. * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} + * @param isUnlock If the type is {@link BiometricUnlockController#isBiometricUnlock()} () */ - public void onBiometricAuthModeChanged(boolean wakeAndUnlock) { + public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) { if (mLockIcon != null) { - mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock); + mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java index 01498e6bd54d..f61b556e22ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java @@ -50,5 +50,6 @@ public interface KeyguardMonitor extends CallbackController<Callback> { interface Callback { void onKeyguardShowingChanged(); + default void onKeyguardFadingAwayChanged() {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java index b53ff0e45cea..68d00708b0d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java @@ -141,14 +141,24 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback } public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) { - mKeyguardFadingAway = true; + setKeyguardFadingAway(true); mKeyguardFadingAwayDelay = delay; mKeyguardFadingAwayDuration = fadeoutDuration; } + private void setKeyguardFadingAway(boolean keyguardFadingAway) { + if (mKeyguardFadingAway != keyguardFadingAway) { + mKeyguardFadingAway = keyguardFadingAway; + ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks); + for (int i = 0; i < callbacks.size(); i++) { + callbacks.get(i).onKeyguardFadingAwayChanged(); + } + } + } + public void notifyKeyguardDoneFading() { - mKeyguardFadingAway = false; mKeyguardGoingAway = false; + setKeyguardFadingAway(false); } @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index 6891f562a3c4..3330d1e6d0a8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -20,14 +20,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; -import android.provider.DeviceConfig; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -52,8 +50,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @SmallTest @RunWith(AndroidTestingRunner.class) // Need to run tests on main looper because LiveData operations such as setData, observe, @@ -67,7 +63,7 @@ public final class ClockManagerTest extends SysuiTestCase { private static final int SECONDARY_USER_ID = 11; private static final Uri SETTINGS_URI = null; - ClockManager mClockManager; + private ClockManager mClockManager; private ContentObserver mContentObserver; private DockManagerFake mFakeDockManager; private MutableLiveData<Integer> mCurrentUser; @@ -144,33 +140,6 @@ public final class ClockManagerTest extends SysuiTestCase { } @Test - public void getCurrentClock_inBlackList() { - mClockManager = spy(mClockManager); - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID); - // GIVEN that bubble clock is in blacklist - when(mClockManager.getBlackListFromConfig()).thenReturn(BUBBLE_CLOCK); - // WHEN device config change of systemui is fired - mClockManager.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); - // THEN the result is null, indicated the current clock should be reset to the default one. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getClockInfo_inBlackList() { - mClockManager = spy(mClockManager); - // GIVEN that bubble clock is in blacklist - when(mClockManager.getBlackListFromConfig()).thenReturn(BUBBLE_CLOCK); - // WHEN device config change of systemui is fired - mClockManager.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI); - // THEN the ClockInfo should not contain bubble clock - List<ClockInfo> clocks = mClockManager.getClockInfos(); - assertThat(clocks.stream().anyMatch(info -> BUBBLE_CLOCK.equals(info.getId()))).isFalse(); - } - - @Test public void onClockChanged_customClock() { // GIVEN that settings is set to the bubble clock face when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index 010b85edacdd..58fb53aae7bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -20,12 +20,16 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -79,13 +83,19 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { @Mock private VisualStabilityManager mVisualStabilityManager; @Mock private ShadeController mShadeController; + private TestableLooper mTestableLooper; + private Handler mHandler; private NotificationViewHierarchyManager mViewHierarchyManager; private NotificationTestHelper mHelper; + private boolean mMadeReentrantCall = false; @Before public void setUp() { MockitoAnnotations.initMocks(this); - Assert.sMainLooper = TestableLooper.get(this).getLooper(); + mTestableLooper = TestableLooper.get(this); + Assert.sMainLooper = mTestableLooper.getLooper(); + mHandler = Handler.createAsync(mTestableLooper.getLooper()); + mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); @@ -98,7 +108,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { when(mEntryManager.getNotificationData()).thenReturn(mNotificationData); mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, - mLockscreenUserManager, mGroupManager, mVisualStabilityManager, + mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, mock(StatusBarStateControllerImpl.class), mEntryManager, () -> mShadeController, new BubbleData(mContext), mock(KeyguardBypassController.class), @@ -215,9 +225,60 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { verify(entry0.getRow(), times(1)).showAppOpsIcons(any()); } + @Test + public void testReentrantCallsToOnDynamicPrivacyChangedPostForLater() { + // GIVEN a ListContainer that will make a re-entrant call to updateNotificationViews() + mMadeReentrantCall = false; + doAnswer((invocation) -> { + if (!mMadeReentrantCall) { + mMadeReentrantCall = true; + mViewHierarchyManager.onDynamicPrivacyChanged(); + } + return null; + }).when(mListContainer).setMaxDisplayedNotifications(anyInt()); + + // WHEN we call updateNotificationViews() + mViewHierarchyManager.updateNotificationViews(); + + // THEN onNotificationViewUpdateFinished() is only called once + verify(mListContainer).onNotificationViewUpdateFinished(); + + // WHEN we drain the looper + mTestableLooper.processAllMessages(); + + // THEN updateNotificationViews() is called a second time (for the reentrant call) + verify(mListContainer, times(2)).onNotificationViewUpdateFinished(); + } + + @Test + public void testMultipleReentrantCallsToOnDynamicPrivacyChangedOnlyPostOnce() { + // GIVEN a ListContainer that will make many re-entrant calls to updateNotificationViews() + mMadeReentrantCall = false; + doAnswer((invocation) -> { + if (!mMadeReentrantCall) { + mMadeReentrantCall = true; + mViewHierarchyManager.onDynamicPrivacyChanged(); + mViewHierarchyManager.onDynamicPrivacyChanged(); + mViewHierarchyManager.onDynamicPrivacyChanged(); + mViewHierarchyManager.onDynamicPrivacyChanged(); + } + return null; + }).when(mListContainer).setMaxDisplayedNotifications(anyInt()); + + // WHEN we call updateNotificationViews() and drain the looper + mViewHierarchyManager.updateNotificationViews(); + verify(mListContainer).onNotificationViewUpdateFinished(); + clearInvocations(mListContainer); + mTestableLooper.processAllMessages(); + + // THEN updateNotificationViews() is called only one more time + verify(mListContainer).onNotificationViewUpdateFinished(); + } + private class FakeListContainer implements NotificationListContainer { final LinearLayout mLayout = new LinearLayout(mContext); final List<View> mRows = Lists.newArrayList(); + private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications; @Override public void setChildTransferInProgress(boolean childTransferInProgress) {} @@ -266,7 +327,11 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { } @Override - public void setMaxDisplayedNotifications(int maxNotifications) {} + public void setMaxDisplayedNotifications(int maxNotifications) { + if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) { + mViewHierarchyManager.onDynamicPrivacyChanged(); + } + } @Override public ViewGroup getViewParentForNotification(NotificationEntry entry) { @@ -301,5 +366,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { return false; } + @Override + public void onNotificationViewUpdateFinished() { } } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index e2b59b45e1e6..c2f452932775 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -108,7 +108,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub .replaceWith("?"); private ByteBuffer mUtf8BufferStat = ByteBuffer.allocateDirect(MAX_LOW_POWER_STATS_SIZE); private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE); - private static final int MAX_LOW_POWER_STATS_SIZE = 2048; + private static final int MAX_LOW_POWER_STATS_SIZE = 4096; /** * Replaces the information in the given rpmStats with up-to-date information. diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 42802f6c3cec..d1b5534e3777 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -1208,14 +1208,22 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - // Traffic occurring on stacked interfaces is usually clatd, - // which is already accounted against its final egress interface - // by the kernel. Thus, we only need to collect stacked - // interface stats at the UID level. + // Traffic occurring on stacked interfaces is usually clatd. + // UID stats are always counted on the stacked interface and never + // on the base interface, because the packets on the base interface + // do not actually match application sockets until they are translated. + // + // Interface stats are more complicated. Packets subject to BPF offload + // never appear on the base interface and only appear on the stacked + // interface, so to ensure those packets increment interface stats, interface + // stats from stacked interfaces must be collected. final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks(); for (LinkProperties stackedLink : stackedLinks) { final String stackedIface = stackedLink.getInterfaceName(); if (stackedIface != null) { + if (mUseBpfTrafficStats) { + findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident); + } findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident); if (isMobile) { mobileIfaces.add(stackedIface); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2052b1570f34..eba4fb63bfc6 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2523,11 +2523,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } final int resource; - if (onWallpaper) { - resource = R.anim.lock_screen_behind_enter_wallpaper; - } else if (subtleAnimation) { + if (subtleAnimation) { resource = R.anim.lock_screen_behind_enter_subtle; - } else { + } else if (onWallpaper) { + resource = R.anim.lock_screen_behind_enter_wallpaper; + } else { resource = R.anim.lock_screen_behind_enter; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0faea61b9d60..bc67cb04b5bc 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3162,7 +3162,7 @@ final class ActivityRecord extends ConfigurationContainer { boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) { return ensureActivityConfiguration(globalChanges, preserveWindow, - false /* ignoreStopState */); + false /* ignoreVisibility */); } /** @@ -3172,15 +3172,15 @@ final class ActivityRecord extends ConfigurationContainer { * @param globalChanges The changes to the global configuration. * @param preserveWindow If the activity window should be preserved on screen if the activity * is relaunched. - * @param ignoreStopState If we should try to relaunch the activity even if it is in the stopped - * state. This is useful for the case where we know the activity will be - * visible soon and we want to ensure its configuration before we make it - * visible. + * @param ignoreVisibility If we should try to relaunch the activity even if it is invisible + * (stopped state). This is useful for the case where we know the + * activity will be visible soon and we want to ensure its configuration + * before we make it visible. * @return False if the activity was relaunched and true if it wasn't relaunched because we * can't or the app handles the specific configuration that is changing. */ boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow, - boolean ignoreStopState) { + boolean ignoreVisibility) { final ActivityStack stack = getActivityStack(); if (stack.mConfigWillChange) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, @@ -3196,15 +3196,9 @@ final class ActivityRecord extends ConfigurationContainer { return true; } - if (!ignoreStopState && (mState == STOPPING || mState == STOPPED)) { + if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Skipping config check stopped or stopping: " + this); - return true; - } - - if (!shouldBeVisible()) { - if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, - "Skipping config check invisible stack: " + this); + "Skipping config check invisible: " + this); return true; } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index c4e0db46f4da..46f86ee5e9f9 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -164,6 +164,8 @@ import com.android.server.am.AppTimeTracker; import com.android.server.am.EventLogTags; import com.android.server.am.PendingIntentRecord; +import com.google.android.collect.Sets; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -1880,8 +1882,7 @@ class ActivityStack extends ConfigurationContainer { mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); } - private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, - String reason) { + void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed, String reason) { if (!mStackSupervisor.mStoppingActivities.contains(r)) { EventLog.writeEvent(EventLogTags.AM_ADD_TO_STOPPING, r.mUserId, System.identityHashCode(r), r.shortComponentName, reason); @@ -2176,7 +2177,7 @@ class ActivityStack extends ConfigurationContainer { // sure it matches the current configuration. if (r != starting && notifyClients) { r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows, - true /* ignoreStopState */); + true /* ignoreVisibility */); } if (!r.attachedToProcess()) { @@ -3138,8 +3139,10 @@ class ActivityStack extends ConfigurationContainer { boolean newTask, boolean keepCurTransition, ActivityOptions options) { TaskRecord rTask = r.getTaskRecord(); final int taskId = rTask.taskId; + final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront(); // mLaunchTaskBehind tasks get placed at the back of the task stack. - if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) { + if (!r.mLaunchTaskBehind && allowMoveToFront + && (taskForIdLocked(taskId) == null || newTask)) { // Last activity in task had been removed or ActivityManagerService is reusing task. // Insert or replace. // Might not even be in. @@ -3198,7 +3201,9 @@ class ActivityStack extends ConfigurationContainer { task.setFrontOfTask(); - if (!isHomeOrRecentsStack() || numActivities() > 0) { + // The transition animation and starting window are not needed if {@code allowMoveToFront} + // is false, because the activity won't be visible. + if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) { final DisplayContent dc = getDisplay().mDisplayContent; if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: starting " + r); @@ -4021,6 +4026,14 @@ class ActivityStack extends ConfigurationContainer { } getDisplay().mDisplayContent.prepareAppTransition(transit, false); + // When finishing the activity pre-emptively take the snapshot before the app window + // is marked as hidden and any configuration changes take place + if (mWindowManager.mTaskSnapshotController != null) { + final ArraySet<Task> tasks = Sets.newArraySet(task.mTask); + mWindowManager.mTaskSnapshotController.snapshotTasks(tasks); + mWindowManager.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(tasks); + } + // Tell window manager to prepare for this one to be removed. r.setVisibility(false); diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index c992a69c2ecb..19916bc617f4 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -415,7 +415,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { void sendErrorResult(String message) { try { - if (callerApp.hasThread()) { + if (callerApp != null && callerApp.hasThread()) { callerApp.getThread().scheduleCrash(message); } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 0a88eef86ea8..eda9d24ef3c5 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2307,12 +2307,12 @@ class ActivityStarter { // isLockTaskModeViolation fails below. if (mReuseTask == null) { + final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront; final TaskRecord task = mTargetStack.createTaskRecord( mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, - mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord, - mOptions); + mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); updateBounds(mStartActivity.getTaskRecord(), mLaunchParams.mBounds); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a70ea607cf26..0f96f99c3348 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1451,9 +1451,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .execute(); } + /** + * Start the recents activity to perform the recents animation. + * + * @param intent The intent to start the recents activity. + * @param recentsAnimationRunner Pass {@code null} to only preload the activity. + */ @Override - public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver, - IRecentsAnimationRunner recentsAnimationRunner) { + public void startRecentsActivity(Intent intent, @Deprecated IAssistDataReceiver unused, + @Nullable IRecentsAnimationRunner recentsAnimationRunner) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()"); final int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); @@ -1464,9 +1470,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Start a new recents animation final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor, - getActivityStartController(), mWindowManager, callingPid); - anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent, - recentsUid, assistDataReceiver); + getActivityStartController(), mWindowManager, intent, recentsComponent, + recentsUid, callingPid); + if (recentsAnimationRunner == null) { + anim.preloadRecentsActivity(); + } else { + anim.startRecentsActivity(recentsAnimationRunner); + } } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 6127303141f4..553b0ffa6999 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -176,6 +176,8 @@ class DragState { mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token); mTransferTouchFromToken = null; + // syncInputWindows here to ensure the input channel isn't removed before the transfer. + mTransaction.syncInputWindows(); mTransaction.apply(); } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 434239f6ecf7..bf627ec5680c 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -34,7 +34,6 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_T import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS; import android.app.ActivityOptions; -import android.app.IAssistDataReceiver; import android.content.ComponentName; import android.content.Intent; import android.os.RemoteException; @@ -58,7 +57,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, private final ActivityStartController mActivityStartController; private final WindowManagerService mWindowManager; private final ActivityDisplay mDefaultDisplay; + private final Intent mTargetIntent; + private final ComponentName mRecentsComponent; + private final int mRecentsUid; private final int mCallingPid; + private final int mUserId; + private final int mTargetActivityType; /** * The activity which has been launched behind. We need to remember the activity because the @@ -66,27 +70,90 @@ class RecentsAnimation implements RecentsAnimationCallbacks, * for the exact activity. */ private ActivityRecord mLaunchedTargetActivity; - private int mTargetActivityType; // The stack to restore the target stack behind when the animation is finished private ActivityStack mRestoreTargetBehindStack; RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor, ActivityStartController activityStartController, WindowManagerService wm, - int callingPid) { + Intent targetIntent, ComponentName recentsComponent, int recentsUid, int callingPid) { mService = atm; mStackSupervisor = stackSupervisor; mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay(); mActivityStartController = activityStartController; mWindowManager = wm; + mTargetIntent = targetIntent; + mRecentsComponent = recentsComponent; + mRecentsUid = recentsUid; mCallingPid = callingPid; + mUserId = atm.getCurrentUserId(); + mTargetActivityType = targetIntent.getComponent() != null + && recentsComponent.equals(targetIntent.getComponent()) + ? ACTIVITY_TYPE_RECENTS + : ACTIVITY_TYPE_HOME; + } + + /** + * Starts the recents activity in background without animation if the record doesn't exist or + * the client isn't launched. If the recents activity is already alive, ensure its configuration + * is updated to the current one. + */ + void preloadRecentsActivity() { + if (DEBUG) Slog.d(TAG, "Preload recents with " + mTargetIntent); + ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, + mTargetActivityType); + ActivityRecord targetActivity = getTargetActivity(targetStack); + if (targetActivity != null) { + if (targetActivity.visible || targetActivity.isTopRunningActivity()) { + // The activity is ready. + return; + } + if (targetActivity.attachedToProcess()) { + // The activity may be relaunched if it cannot handle the current configuration + // changes. The activity will be paused state if it is relaunched, otherwise it + // keeps the original stopped state. + targetActivity.ensureActivityConfiguration(0 /* globalChanges */, + false /* preserveWindow */, true /* ignoreVisibility */); + if (DEBUG) Slog.d(TAG, "Updated config=" + targetActivity.getConfiguration()); + } + } else { + // Create the activity record. Because the activity is invisible, this doesn't really + // start the client. + startRecentsActivityInBackground("preloadRecents"); + targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); + targetActivity = getTargetActivity(targetStack); + if (targetActivity == null) { + Slog.w(TAG, "Cannot start " + mTargetIntent); + return; + } + } + + if (!targetActivity.attachedToProcess()) { + if (DEBUG) Slog.d(TAG, "Real start recents"); + mStackSupervisor.startSpecificActivityLocked(targetActivity, false /* andResume */, + false /* checkConfig */); + // Make sure the activity won't be involved in transition. + if (targetActivity.mAppWindowToken != null) { + targetActivity.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController + .appRemovedOrHidden(targetActivity.mAppWindowToken); + } + } + + // Invisible activity should be stopped. If the recents activity is alive and its doesn't + // need to relaunch by current configuration, then it may be already in stopped state. + if (!targetActivity.isState(ActivityStack.ActivityState.STOPPING, + ActivityStack.ActivityState.STOPPED)) { + // Add to stopping instead of stop immediately. So the client has the chance to perform + // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more + // things (e.g. the measure can be done earlier). The actual stop will be performed when + // it reports idle. + targetStack.addToStopping(targetActivity, true /* scheduleIdle */, + true /* idleDelayed */, "preloadRecents"); + } } - void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner, - ComponentName recentsComponent, int recentsUid, - @Deprecated IAssistDataReceiver assistDataReceiver) { - if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent - + " assistDataReceiver=" + assistDataReceiver); + void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) { + if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + mTargetIntent); Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity"); // TODO(multi-display) currently only support recents animation in default display. @@ -100,15 +167,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } // If the activity is associated with the recents stack, then try and get that first - final int userId = mService.getCurrentUserId(); - mTargetActivityType = intent.getComponent() != null - && recentsComponent.equals(intent.getComponent()) - ? ACTIVITY_TYPE_RECENTS - : ACTIVITY_TYPE_HOME; ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); - ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent(), - userId); + ActivityRecord targetActivity = getTargetActivity(targetStack); final boolean hasExistingActivity = targetActivity != null; if (hasExistingActivity) { final ActivityDisplay display = targetActivity.getDisplay(); @@ -127,7 +188,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, true /* forceSend */, targetActivity); } - mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent); + mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent); mService.mH.post(() -> mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true)); @@ -148,23 +209,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } } else { // No recents activity, create the new recents activity bottom most - ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchActivityType(mTargetActivityType); - options.setAvoidMoveToFront(); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); - - mActivityStartController - .obtainStarter(intent, "startRecentsActivity_noTargetActivity") - .setCallingUid(recentsUid) - .setCallingPackage(recentsComponent.getPackageName()) - .setActivityOptions(SafeActivityOptions.fromBundle(options.toBundle())) - .setMayWait(userId) - .execute(); + startRecentsActivityInBackground("startRecentsActivity_noTargetActivity"); // Move the recents activity into place for the animation targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); - targetActivity = getTargetActivity(targetStack, intent.getComponent(), userId); + targetActivity = getTargetActivity(targetStack); mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack); if (DEBUG) { Slog.d(TAG, "Moved stack=" + targetStack + " behind stack=" @@ -176,7 +226,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // TODO: Maybe wait for app to draw in this particular case? - if (DEBUG) Slog.d(TAG, "Started intent=" + intent); + if (DEBUG) Slog.d(TAG, "Started intent=" + mTargetIntent); } // Mark the target activity as launch-behind to bump its visibility for the @@ -383,6 +433,21 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } } + private void startRecentsActivityInBackground(String reason) { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchActivityType(mTargetActivityType); + options.setAvoidMoveToFront(); + mTargetIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); + + mActivityStartController + .obtainStarter(mTargetIntent, reason) + .setCallingUid(mRecentsUid) + .setCallingPackage(mRecentsComponent.getPackageName()) + .setActivityOptions(new SafeActivityOptions(options)) + .setMayWait(mUserId) + .execute(); + } + /** * Called only when the animation should be canceled prior to starting. */ @@ -412,15 +477,15 @@ class RecentsAnimation implements RecentsAnimationCallbacks, * @return the top activity in the {@param targetStack} matching the {@param component}, or just * the top activity of the top task if no task matches the component. */ - private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component, - int userId) { + private ActivityRecord getTargetActivity(ActivityStack targetStack) { if (targetStack == null) { return null; } for (int i = targetStack.getChildCount() - 1; i >= 0; i--) { final TaskRecord task = targetStack.getChildAt(i); - if (task.userId == userId && task.getBaseIntent().getComponent().equals(component)) { + if (task.userId == mUserId + && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent())) { return task.getTopActivity(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 4986a6d5bd0d..ecf3acd32d4f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -85,6 +85,7 @@ import org.mockito.invocation.InvocationOnMock; import java.io.File; import java.util.List; +import java.util.function.Consumer; /** * A base class to handle common operations in activity related unit tests. @@ -169,8 +170,12 @@ class ActivityTestsBase { * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy * when starting activity in unit tests. */ - void mockTaskRecordFactory() { - final TaskRecord task = new TaskBuilder(mSupervisor).setCreateStack(false).build(); + void mockTaskRecordFactory(Consumer<TaskBuilder> taskBuilderSetup) { + final TaskBuilder taskBuilder = new TaskBuilder(mSupervisor).setCreateStack(false); + if (taskBuilderSetup != null) { + taskBuilderSetup.accept(taskBuilder); + } + final TaskRecord task = taskBuilder.build(); final TaskRecordFactory factory = mock(TaskRecordFactory.class); TaskRecord.setTaskRecordFactory(factory); doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */, @@ -178,6 +183,10 @@ class ActivityTestsBase { any() /* voiceInteractor */); } + void mockTaskRecordFactory() { + mockTaskRecordFactory(null /* taskBuilderSetup */); + } + /** * Builder for creating new activities. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 1f8b33eb5bb4..9630b7d46e3c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -42,8 +42,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.platform.test.annotations.Presubmit; import android.view.IRecentsAnimationRunner; @@ -111,6 +114,57 @@ public class RecentsAnimationTest extends ActivityTestsBase { } @Test + public void testPreloadRecentsActivity() { + // Ensure that the fake recent component can be resolved by the recents intent. + mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent)); + ActivityInfo aInfo = new ActivityInfo(); + aInfo.applicationInfo = new ApplicationInfo(); + aInfo.applicationInfo.uid = 10001; + aInfo.applicationInfo.targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion; + aInfo.packageName = aInfo.applicationInfo.packageName = mRecentsComponent.getPackageName(); + aInfo.processName = "recents"; + doReturn(aInfo).when(mSupervisor).resolveActivity(any() /* intent */, any() /* rInfo */, + anyInt() /* startFlags */, any() /* profilerInfo */); + + // Assume its process is alive because the caller should be the recents service. + WindowProcessController wpc = new WindowProcessController(mService, aInfo.applicationInfo, + aInfo.processName, aInfo.applicationInfo.uid, 0 /* userId */, + mock(Object.class) /* owner */, mock(WindowProcessListener.class)); + wpc.setThread(mock(IApplicationThread.class)); + doReturn(wpc).when(mService).getProcessController(eq(wpc.mName), eq(wpc.mUid)); + + Intent recentsIntent = new Intent().setComponent(mRecentsComponent); + // Null animation indicates to preload. + mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, + null /* recentsAnimationRunner */); + + ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + ActivityStack recentsStack = display.getStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_RECENTS); + assertThat(recentsStack).isNotNull(); + + ActivityRecord recentsActivity = recentsStack.getTopActivity(); + // The activity is started in background so it should be invisible and will be stopped. + assertThat(recentsActivity).isNotNull(); + assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity); + assertFalse(recentsActivity.visible); + + // Assume it is stopped to test next use case. + recentsActivity.activityStoppedLocked(null /* newIcicle */, null /* newPersistentState */, + null /* description */); + mSupervisor.mStoppingActivities.remove(recentsActivity); + + spyOn(recentsActivity); + // Start when the recents activity exists. It should ensure the configuration. + mService.startRecentsActivity(recentsIntent, null /* assistDataReceiver */, + null /* recentsAnimationRunner */); + + verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */, + anyBoolean() /* preserveWindow */, eq(true) /* ignoreVisibility */); + assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity); + } + + @Test public void testRestartRecentsActivity() throws Exception { // Have a recents activity that is not attached to its process (ActivityRecord.app = null). ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 19f8203f7502..ef967d71cfa0 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -836,6 +836,19 @@ public class CarrierConfigManager { "carrier_metered_roaming_apn_types_strings"; /** + * APN types that are not allowed on cellular + * @hide + */ + public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY = + "carrier_wwan_disallowed_apn_types_string_array"; + + /** + * APN types that are not allowed on IWLAN + * @hide + */ + public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY = + "carrier_wlan_disallowed_apn_types_string_array"; + /** * CDMA carrier ERI (Enhanced Roaming Indicator) file name * @hide */ @@ -3133,6 +3146,10 @@ public class CarrierConfigManager { new String[]{"default", "mms", "dun", "supl"}); sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS, new String[]{"default", "mms", "dun", "supl"}); + sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY, + new String[]{""}); + sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY, + new String[]{""}); sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY, new int[]{ 4, /* IS95A */ diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 4a9b174cedbb..484fd3b17c02 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2121,27 +2121,24 @@ public class SubscriptionManager { * @hide */ @UnsupportedAppUsage - public static @NonNull int[] getActiveSubscriptionIdList() { - return getActiveSubscriptionIdList(true); - } + public @NonNull int[] getActiveSubscriptionIdList() { + int[] subId = null; - /** - * @return a non-null list of subId's that are active. - * - * @hide - */ - public static @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) { try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - int[] subId = iSub.getActiveSubIdList(visibleOnly); - if (subId != null) return subId; + subId = iSub.getActiveSubIdList(/*visibleOnly*/true); } } catch (RemoteException ex) { // ignore it } - return new int[0]; + if (subId == null) { + subId = new int[0]; + } + + return subId; + } /** diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 5b57c9d7554c..7a0ab9ca6a28 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -605,11 +605,15 @@ public final class TelephonyPermissions { */ private static boolean checkCarrierPrivilegeForAnySubId(Context context, Supplier<ITelephony> telephonySupplier, int uid) { - int[] activeSubIds = SubscriptionManager.getActiveSubscriptionIdList(/*visibleOnly*/ false); - for (int activeSubId : activeSubIds) { - if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) - == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { - return true; + SubscriptionManager sm = (SubscriptionManager) context.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + int[] activeSubIds = sm.getActiveSubscriptionIdList(); + if (activeSubIds != null) { + for (int activeSubId : activeSubIds) { + if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } } } return false; |