Merge "Connect getStatus/getStatusExt between TunerFrontend and FrontendClient" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f1..86364af 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@
"android.hardware.vibrator-V1.3-java",
"framework-protos",
"stable.core.platform.api.stubs",
- // There are a few classes from modules used as type arguments that
- // need to be resolved by metalava. For now, we can use a previously
- // finalized stub library to resolve them. If a new class gets added,
- // this may be need to be revisited to use a manually maintained stub
- // library with empty classes in order to resolve those references.
- "sdk_system_30_android",
+ // There are a few classes from modules used by the core that
+ // need to be resolved by metalava. We use a prebuilt stub of the
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
],
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
@@ -404,7 +405,11 @@
"android_defaults_stubs_current",
"android_stubs_dists_default",
],
- libs: ["sdk_system_29_android"],
+ libs: [
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
static_libs: ["art.module.public.api.stubs"],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8a2817..d249f2a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
package com.android.server.job.controllers;
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -332,7 +331,7 @@
if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -350,7 +349,7 @@
if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
// If we don't know the bandwidth, all we can do is hope the job finishes in time.
- if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+ if (bandwidth > 0) {
// Divide by 8 to convert bits to bytes.
final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
/ (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -380,18 +379,16 @@
private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
- final NetworkCapabilities required;
// A restricted job that's out of quota MUST use an unmetered network.
if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
&& !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
- required = new NetworkCapabilities(
+ final NetworkCapabilities required = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .addCapability(NET_CAPABILITY_NOT_METERED);
+ .addCapability(NET_CAPABILITY_NOT_METERED).build();
+ return required.satisfiedByNetworkCapabilities(capabilities);
} else {
- required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+ return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
}
-
- return required.satisfiedByNetworkCapabilities(capabilities);
}
private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -402,9 +399,9 @@
}
// See if we match after relaxing any unmetered request
- final NetworkCapabilities relaxed = new NetworkCapabilities(
+ final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
jobStatus.getJob().getRequiredNetwork().networkCapabilities)
- .removeCapability(NET_CAPABILITY_NOT_METERED);
+ .removeCapability(NET_CAPABILITY_NOT_METERED).build();
if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
// TODO: treat this as "maybe" response; need to check quotas
return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/core/api/current.txt b/core/api/current.txt
index a945889..5679a3d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12298,7 +12298,7 @@
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
- method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+ method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12318,7 +12318,6 @@
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
- field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12504,6 +12503,10 @@
ctor public PackageManager.NameNotFoundException(String);
}
+ @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+ method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+ }
+
public static final class PackageManager.Property implements android.os.Parcelable {
method public int describeContents();
method public boolean getBoolean();
@@ -15025,6 +15028,7 @@
field public static final int RGB_565 = 4; // 0x4
field public static final int UNKNOWN = 0; // 0x0
field public static final int Y8 = 538982489; // 0x20203859
+ field public static final int YCBCR_P010 = 54; // 0x36
field public static final int YUV_420_888 = 35; // 0x23
field public static final int YUV_422_888 = 39; // 0x27
field public static final int YUV_444_888 = 40; // 0x28
@@ -31444,6 +31448,7 @@
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
+ method public static boolean isHeadlessSystemUserMode();
method public boolean isManagedProfile();
method public boolean isQuietModeEnabled(android.os.UserHandle);
method public boolean isSystemUser();
@@ -40390,6 +40395,7 @@
field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+ field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
field public static final String KEY_PREFIX = "ims.";
field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1977babe..053f6cb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10331,7 +10331,7 @@
ctor public ExternalStorageService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
- method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+ method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11704,6 +11704,7 @@
method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11711,6 +11712,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e88b6b9..79917d0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -28,6 +28,7 @@
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
+ field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -119,6 +120,7 @@
public class ActivityOptions {
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+ method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public static void setExitTransitionTimeout(long);
method public void setLaunchActivityType(int);
method public void setLaunchTaskId(int);
@@ -390,11 +392,10 @@
method public boolean isFactoryResetProtectionPolicySupported();
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
- field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
- field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2b5e18d..28da1c3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,6 +17,7 @@
package android.app;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -310,6 +311,9 @@
private static final String KEY_REMOTE_TRANSITION =
"android:activity.remoteTransition";
+ private static final String KEY_OVERRIDE_TASK_TRANSITION =
+ "android:activity.overrideTaskTransition";
+
/**
* @see #setLaunchCookie
* @hide
@@ -393,6 +397,7 @@
private RemoteAnimationAdapter mRemoteAnimationAdapter;
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
+ private boolean mOverrideTaskTransition;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -476,6 +481,40 @@
}
/**
+ * Create an ActivityOptions specifying a custom animation to run when the activity in the
+ * different task is displayed.
+ *
+ * @param context Who is defining this. This is the application that the
+ * animation resources will be loaded from.
+ * @param enterResId A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitResId A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ * @param handler If <var>listener</var> is non-null this must be a valid
+ * Handler on which to dispatch the callback; otherwise it should be null.
+ * @param startedListener Optional OnAnimationStartedListener to find out when the
+ * requested animation has started running. If for some reason the animation
+ * is not executed, the callback will happen immediately.
+ * @param finishedListener Optional OnAnimationFinishedListener when the animation
+ * has finished running.
+ *
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ * @hide
+ */
+ @RequiresPermission(START_TASKS_FROM_RECENTS)
+ @TestApi
+ public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context,
+ int enterResId, int exitResId, @Nullable Handler handler,
+ @Nullable OnAnimationStartedListener startedListener,
+ @Nullable OnAnimationFinishedListener finishedListener) {
+ ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
+ startedListener, finishedListener);
+ opts.mOverrideTaskTransition = true;
+ return opts;
+ }
+
+ /**
* Creates an ActivityOptions specifying a custom animation to run in place on an existing
* activity.
*
@@ -1107,6 +1146,7 @@
mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
+ mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
}
/**
@@ -1561,6 +1601,12 @@
return mLaunchCookie;
}
+
+ /** @hide */
+ public boolean getOverrideTaskTransition() {
+ return mOverrideTaskTransition;
+ }
+
/**
* Update the current values in this ActivityOptions from those supplied
* in <var>otherOptions</var>. Any values
@@ -1789,6 +1835,9 @@
if (mRemoteTransition != null) {
b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder());
}
+ if (mOverrideTaskTransition) {
+ b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
+ }
return b;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac..8ac9139 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(trustedInstallers);
try {
if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@
"trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ "list of certificates.");
}
+ IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+ new IOnChecksumsReadyListener.Stub() {
+ @Override
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ onChecksumsReadyListener.onChecksumsReady(checksums);
+ }
+ };
mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
- encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+ encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+ getUserId());
} catch (ParcelableException e) {
e.maybeRethrow(PackageManager.NameNotFoundException.class);
throw new RuntimeException(e);
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 14ed414..cbe2995 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -15,8 +15,7 @@
*/
package android.app;
-import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
+import static android.view.WindowManagerImpl.createWindowContextWindowManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +27,8 @@
import android.os.RemoteException;
import android.view.Display;
import android.view.IWindowManager;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import android.view.WindowManagerImpl;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,10 +45,10 @@
*/
@UiContext
public class WindowContext extends ContextWrapper {
- private final WindowManagerImpl mWindowManager;
+ private final WindowManager mWindowManager;
private final IWindowManager mWms;
private final WindowTokenClient mToken;
- private boolean mOwnsToken;
+ private boolean mListenerRegistered;
/**
* Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
@@ -86,25 +85,14 @@
mToken.attachContext(this);
- mWindowManager = new WindowManagerImpl(this);
- mWindowManager.setDefaultToken(mToken);
+ mWindowManager = createWindowContextWindowManager(this);
- int result;
try {
- // Register the token with WindowManager. This will also call back with the current
- // config back to the client.
- result = mWms.addWindowTokenWithOptions(
- mToken, type, getDisplayId(), options, getPackageName());
+ mListenerRegistered = mWms.registerWindowContextListener(mToken, type, getDisplayId(),
+ options);
} catch (RemoteException e) {
- mOwnsToken = false;
throw e.rethrowFromSystemServer();
}
- if (result == ADD_TOO_MANY_TOKENS) {
- throw new UnsupportedOperationException("createWindowContext failed! Too many unused "
- + "window contexts. Please see Context#createWindowContext documentation for "
- + "detail.");
- }
- mOwnsToken = result == ADD_OKAY;
Reference.reachabilityFence(this);
}
@@ -131,10 +119,10 @@
/** Used for test to invoke because we can't invoke finalize directly. */
@VisibleForTesting
public void release() {
- if (mOwnsToken) {
+ if (mListenerRegistered) {
+ mListenerRegistered = false;
try {
- mWms.removeWindowToken(mToken, getDisplayId());
- mOwnsToken = false;
+ mWms.unregisterWindowContextListener(mToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 642bce4..06fe9d7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1730,8 +1730,12 @@
* Broadcast action to notify ManagedProvisioning that
* {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
* @hide
+ * @deprecated No longer needed as ManagedProvisioning no longer handles
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
*/
+ // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
"android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
@@ -5469,26 +5473,6 @@
"android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
/**
- * Broadcast action: notify managed provisioning that the device has been provisioned.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
- "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
- /**
- * Broadcast action: notify managed provisioning that a new managed profile is created.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_PROFILE_CREATED =
- "android.app.action.MANAGED_PROFILE_CREATED";
-
- /**
* Widgets are enabled in keyguard
*/
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -13268,4 +13252,23 @@
}
}
}
+
+ /**
+ * Resets the default cross profile intent filters that were set during
+ * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+ * profiles if any.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ if (mService != null) {
+ try {
+ mService.resetDefaultCrossProfileIntentFilters(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f9ee153..cf0b31e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -498,4 +498,6 @@
UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+ void resetDefaultCrossProfileIntentFilters(int userId);
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883..742d05c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -460,24 +461,40 @@
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes)
throws IOException, XmlPullParserException {
+ verifyTopLevelTag(parser, "full-backup-content");
+
+ parseRules(parser, excludes, includes, Optional.empty());
+
+ logParsingResults(excludes, includes);
+ }
+
+ private void verifyTopLevelTag(XmlPullParser parser, String tag)
+ throws XmlPullParserException, IOException {
int event = parser.getEventType(); // START_DOCUMENT
while (event != XmlPullParser.START_TAG) {
event = parser.next();
}
- if (!"full-backup-content".equals(parser.getName())) {
+ if (!tag.equals(parser.getName())) {
throw new XmlPullParserException("Xml file didn't start with correct tag" +
- " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+ " (" + tag + " ). Found \"" + parser.getName() + "\"");
}
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "====================================================");
- Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+ Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
Log.v(TAG_XML_PARSER, "====================================================");
Log.v(TAG_XML_PARSER, "");
}
+ }
+ private void parseRules(XmlPullParser parser,
+ Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes,
+ Optional<Integer> maybeRequiredFlags)
+ throws IOException, XmlPullParserException {
+ int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
switch (event) {
case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@
break;
}
- int requiredFlags = 0; // no transport flags are required by default
- if (TAG_INCLUDE.equals(parser.getName())) {
- // requiredFlags are only supported for <include /> tag, for <exclude />
- // we should always leave them as the default = 0
- requiredFlags = getRequiredFlagsFromString(
- parser.getAttributeValue(null, "requireFlags"));
- }
+ int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
// retrieve the include/exclude set we'll be adding this rule to
Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@
// Special case for sharedpref files (not dirs) also add ".xml" suffix file.
if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
- !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+ !canonicalFile.getCanonicalPath().endsWith(".xml")) {
final String canonicalXmlPath =
canonicalFile.getCanonicalPath() + ".xml";
activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@
}
}
}
+ }
+
+ private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes) {
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@
return flags;
}
+ private int getRequiredFlagsForRule(XmlPullParser parser,
+ Optional<Integer> maybeRequiredFlags) {
+ if (maybeRequiredFlags.isPresent()) {
+ // This is the new config format where required flags are specified for the whole
+ // section, not per rule.
+ return maybeRequiredFlags.get();
+ }
+
+ if (TAG_INCLUDE.equals(parser.getName())) {
+ // In the legacy config, requiredFlags are only supported for <include /> tag,
+ // for <exclude /> we should always leave them as the default = 0.
+ return getRequiredFlagsFromString(
+ parser.getAttributeValue(null, "requireFlags"));
+ }
+
+ return 0;
+ }
+
private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 987de3f..5d28216 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -75,6 +75,7 @@
import android.view.DisplayAdjustments;
import android.view.View;
import android.view.ViewDebug;
+import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.view.autofill.AutofillManager.AutofillClient;
@@ -6110,18 +6111,19 @@
*
* // WindowManager.LayoutParams initialization
* ...
+ * // The types used in addView and createWindowContext must match.
* mParams.type = TYPE_APPLICATION_OVERLAY;
* ...
*
- * mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+ * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
* </pre>
*
* <p>
- * This context's configuration and resources are adjusted to a display area where the windows
- * with provided type will be added. <b>Note that all windows associated with the same context
- * will have an affinity and can only be moved together between different displays or areas on a
- * display.</b> If there is a need to add different window types, or non-associated windows,
- * separate Contexts should be used.
+ * This context's configuration and resources are adjusted to an area of the display where
+ * the windows with provided type will be added. <b>Note that all windows associated with the
+ * same context will have an affinity and can only be moved together between different displays
+ * or areas on a display.</b> If there is a need to add different window types, or
+ * non-associated windows, separate Contexts should be used.
* </p>
* <p>
* Creating a window context is an expensive operation. Misuse of this API may lead to a huge
@@ -6129,7 +6131,43 @@
* An approach is to create one window context with specific window type and display and
* use it everywhere it's needed.
* </p>
+ * <p>
+ * After {@link Build.VERSION_CODES#S}, window context provides the capability to receive
+ * configuration changes for existing token by overriding the
+ * {@link android.view.WindowManager.LayoutParams#token token} of the
+ * {@link android.view.WindowManager.LayoutParams} passed in
+ * {@link WindowManager#addView(View, LayoutParams)}. This is useful when an application needs
+ * to attach its window to an existing activity for window token sharing use-case.
+ * </p>
+ * <p>
+ * Note that the window context in {@link Build.VERSION_CODES#R} didn't have this
+ * capability. This is a no-op for the window context in {@link Build.VERSION_CODES#R}.
+ * </p>
+ * Below is sample code to <b>attach an existing token to a window context:</b>
+ * <pre class="prettyprint">
+ * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+ * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ * final Context windowContext = anyContext.createWindowContext(primaryDisplay,
+ * TYPE_APPLICATION, null);
*
+ * // Get an existing token.
+ * final IBinder existingToken = activity.getWindow().getAttributes().token;
+ *
+ * // The types used in addView() and createWindowContext() must match.
+ * final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION);
+ * params.token = existingToken;
+ *
+ * // After WindowManager#addView(), the server side will extract the provided token from
+ * // LayoutParams#token (existingToken in the sample code), and switch to propagate
+ * // configuration changes from the node associated with the provided token.
+ * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+ * </pre>
+ * <p>
+ * Note that using {@link android.app.Application} or {@link android.app.Service} context for
+ * UI-related queries may result in layout or continuity issues on devices with variable screen
+ * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect
+ * the {@link Configuration} changes for the visual container.
+ * </p>
* @param type Window type in {@link WindowManager.LayoutParams}
* @param options A bundle used to pass window-related options
* @return A {@link Context} that can be used to create
@@ -6141,9 +6179,7 @@
* @see #LAYOUT_INFLATER_SERVICE
* @see #WALLPAPER_SERVICE
* @throws UnsupportedOperationException if this {@link Context} does not attach to a display,
- * such as {@link android.app.Application Application} or {@link android.app.Service Service},
- * or the current number of window contexts without adding any view by
- * {@link WindowManager#addView} <b>exceeds five</b>.
+ * such as {@link android.app.Application Application} or {@link android.app.Service Service}.
*/
@UiContext
@NonNull
diff --git a/core/java/android/content/pm/IOnChecksumsReadyListener.aidl b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
new file mode 100644
index 0000000..7963ce1
--- /dev/null
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+ void onChecksumsReady(in List<ApkChecksum> checksums);
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b8829bb..a46876e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -754,7 +755,7 @@
void notifyPackagesReplacedReceived(in String[] packages);
- void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+ void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
//------------------------------------------------------------------------
//
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d09d83f..b95b991b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3858,13 +3858,6 @@
public static final String EXTRA_FAILURE_EXISTING_PERMISSION
= "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
- /**
- * Extra field name for the ID of a package pending verification. Passed to
- * a package verifier and is used to call back to
- * @see #requestChecksums
- */
- public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
/**
* Permission flag: The permission is set in its current state
* by the user and apps can still request it at runtime.
@@ -8709,9 +8702,20 @@
*/
public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
+ /** Listener that gets notified when checksums are available. */
+ @FunctionalInterface
+ public interface OnChecksumsReadyListener {
+ /**
+ * Called when the checksums are available.
+ *
+ * @param checksums array of checksums.
+ */
+ void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+ }
+
/**
* Requesting the checksums for APKs within a package.
- * The checksums will be returned asynchronously via statusReceiver.
+ * The checksums will be returned asynchronously via onChecksumsReadyListener.
*
* By default returns all readily available checksums:
* - enforced by platform,
@@ -8730,15 +8734,14 @@
* {@link #TRUST_ALL} will return checksums from any installer,
* {@link #TRUST_NONE} disables optimized installer-enforced checksums,
* otherwise the list has to be non-empty list of certificates.
- * @param statusReceiver called once when the results are available as
- * {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+ * @param onChecksumsReadyListener called once when the results are available.
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
* @throws IllegalArgumentException if the list of trusted installer certificates is empty.
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
*/
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 84a2acc..b016ed6 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -82,4 +82,5 @@
boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
boolean isUidRestrictedOnMeteredNetworks(int uid);
+ boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ed169e7..3e6237d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -464,6 +464,31 @@
}
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ *
+ * @hide
+ */
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ try {
+ return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check that the given uid is restricted from doing networking on metered networks.
*
* @param uid The target uid.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 77183ac..ea1ce37 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1694,10 +1694,13 @@
}
/**
- * @hide
- * @return Whether the device is running in a headless system user mode. It means the headless
- * user (system user) runs system services and system UI, but is not associated with any real
- * person. Secondary users can be created to be associated with real person.
+ * Checks whether the device is running in a headless system user mode.
+ *
+ * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
+ * services and some system UI, but it is not associated with any real person and additional
+ * users must be created to be associated with real persons.
+ *
+ * @return whether the device is running in a headless system user mode.
*/
public static boolean isHeadlessSystemUserMode() {
return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c36..a750b68 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
*/
- public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+ public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
@@ -202,7 +202,7 @@
RemoteCallback callback) {
mHandler.post(() -> {
try {
- onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+ onFreeCache(StorageManager.convert(volumeUuid), bytes);
sendResult(sessionId, null /* throwable */, callback);
} catch (Throwable t) {
sendResult(sessionId, t, callback);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4168064..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,8 +59,12 @@
* an application window, excluding the system decorations. The application display area may
* be smaller than the real display area because the system subtracts the space needed
* for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations. Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size). Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustments)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+ final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustements)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
}
}
@@ -1213,34 +1217,30 @@
}
/**
- * Provides the largest {@link Point outSize} an app may expect in the current system state,
- * without subtracting any window decor.
+ * Gets the real size of the display without subtracting any window decor or
+ * applying any compatibility scale factors.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
- * </p>
+ * </p><p>
+ * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+ * report the same bounds except that certain areas of the display may not be available to
+ * windows created in the {@link WindowManager}'s {@link Context}.
+ *
+ * For example, imagine a device which has a multi-task mode that limits windows to half of the
+ * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+ * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+ * still reports the bounds of the whole display.
*
* @param outSize Set to the real size of the display.
+ *
+ * @see WindowManager#getMaximumWindowMetrics()
*/
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- final Rect bounds = mResources.getConfiguration()
- .windowConfiguration.getMaxBounds();
- outSize.x = bounds.width();
- outSize.y = bounds.height();
- if (DEBUG) {
- Log.d(TAG, "getRealSize determined from max bounds: " + outSize
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1250,11 +1250,9 @@
}
/**
- * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
- * system state, without subtracting any window decor.
+ * Gets display metrics based on the real size of this display.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
@@ -1265,18 +1263,6 @@
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- mDisplayInfo.getMaxBoundsMetrics(outMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
- mResources.getConfiguration());
- if (DEBUG) {
- Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1286,20 +1272,6 @@
}
/**
- * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
- * display dimensions. The max bounds field may be smaller than the logical dimensions
- * when apps need to be sandboxed.
- * @return {@code true} when max bounds should be applied.
- */
- private boolean shouldReportMaxBounds() {
- if (mResources == null) {
- return false;
- }
- final Configuration config = mResources.getConfiguration();
- return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
- }
-
- /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a44504..2a00b5a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -616,29 +615,11 @@
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
- /**
- * Populates {@code outMetrics} with details of the logical display. Bounds are limited
- * by the logical size of the display.
- *
- * @param outMetrics the {@link DisplayMetrics} to be populated
- * @param compatInfo the {@link CompatibilityInfo} to be applied
- * @param configuration the {@link Configuration}
- */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
- /**
- * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
- * {@link WindowConfiguration#getMaxBounds()}
- */
- public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
- Configuration configuration) {
- Rect bounds = configuration.windowConfiguration.getMaxBounds();
- getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
- }
-
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d..1f64fb8 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@
/**
* Called when the process needs to start the remote animation.
*
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+ void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+ in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
in IRemoteAnimationFinishedCallback finishedCallback);
/**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7843411..ae8afca 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -117,7 +117,7 @@
// These can only be called when holding the MANAGE_APP_TOKENS permission.
void setEventDispatching(boolean enabled);
- /** @return {@code true} if this binder is a registered window token. */
+ /** Returns {@code true} if this binder is a registered window token. */
boolean isWindowToken(in IBinder binder);
/**
* Adds window token for a given type.
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336..6a34a15 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
types, mCallbacks, durationMs, interpolator, animationType, translator);
InsetsAnimationThread.getHandler().post(() -> {
+ if (mControl.isCancelled()) {
+ return;
+ }
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
listener.onReady(mControl, types);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fe6b6e4..219190f 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -604,13 +604,13 @@
return Type.CAPTION_BAR;
case ITYPE_IME:
return Type.IME;
- case ITYPE_TOP_GESTURES:
- case ITYPE_BOTTOM_GESTURES:
case ITYPE_TOP_MANDATORY_GESTURES:
case ITYPE_BOTTOM_MANDATORY_GESTURES:
case ITYPE_LEFT_MANDATORY_GESTURES:
case ITYPE_RIGHT_MANDATORY_GESTURES:
return Type.MANDATORY_SYSTEM_GESTURES;
+ case ITYPE_TOP_GESTURES:
+ case ITYPE_BOTTOM_GESTURES:
case ITYPE_LEFT_GESTURES:
case ITYPE_RIGHT_GESTURES:
return Type.SYSTEM_GESTURES;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 18ef80c..036a703 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -315,7 +315,7 @@
* In that case we receive a call back from {@link ActivityThread} and this flag is used to
* preserve the initial value.
*
- * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+ * @see #performConfigurationChange(MergedConfiguration, boolean, int)
*/
private boolean mForceNextConfigUpdate;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 269ccbb..dc81bc9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -690,10 +690,6 @@
<!-- Made protected in S (was added in R) -->
<protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
- <!-- Added in S -->
- <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
- <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2539,7 +2535,7 @@
<permission android:name="android.permission.REAL_GET_TASKS"
android:protectionLevel="signature|privileged" />
- <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
android:protectionLevel="signature|privileged|recents" />
diff --git a/core/res/res/color-car/car_borderless_button_text_color.xml b/core/res/res/color-car/car_borderless_button_text_color.xml
index 1cdd6cd..0a86e40 100644
--- a/core/res/res/color-car/car_borderless_button_text_color.xml
+++ b/core/res/res/color-car/car_borderless_button_text_color.xml
@@ -16,5 +16,6 @@
<!-- Default text colors for car buttons when enabled/disabled. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@*android:color/car_grey_700" android:state_enabled="false"/>
+ <item android:color="@*android:color/car_grey_700" android:state_ux_restricted="true"/>
<item android:color="?android:attr/colorButtonNormal"/>
</selector>
diff --git a/core/res/res/color-car/car_switch_track.xml b/core/res/res/color-car/car_switch_track.xml
new file mode 100644
index 0000000..8ca67dd
--- /dev/null
+++ b/core/res/res/color-car/car_switch_track.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<!-- copy of switch_track_material, but with a ux restricted state -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:color="?attr/colorForeground"
+ android:alpha="?attr/disabledAlpha" />
+ <item android:state_ux_restricted="true"
+ android:color="?attr/colorForeground"
+ android:alpha="?attr/disabledAlpha" />
+ <item android:state_checked="true"
+ android:color="?attr/colorControlActivated" />
+ <item android:color="?attr/colorForeground" />
+</selector>
diff --git a/core/res/res/drawable-car/car_button_background.xml b/core/res/res/drawable-car/car_button_background.xml
index e568aeb..13b0ec1 100644
--- a/core/res/res/drawable-car/car_button_background.xml
+++ b/core/res/res/drawable-car/car_button_background.xml
@@ -25,6 +25,22 @@
android:color="#0059B3"/>
</shape>
</item>
+ <item android:state_focused="true" android:state_pressed="true" android:state_ux_restricted="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@*android:dimen/car_button_radius"/>
+ <solid android:color="@*android:color/car_grey_300"/>
+ <stroke android:width="4dp"
+ android:color="#0059B3"/>
+ </shape>
+ </item>
+ <item android:state_focused="true" android:state_ux_restricted="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@*android:dimen/car_button_radius"/>
+ <solid android:color="@*android:color/car_grey_300"/>
+ <stroke android:width="8dp"
+ android:color="#0059B3"/>
+ </shape>
+ </item>
<item android:state_focused="true" android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:radius="@*android:dimen/car_button_radius"/>
@@ -47,6 +63,12 @@
<solid android:color="@*android:color/car_grey_300"/>
</shape>
</item>
+ <item android:state_ux_restricted="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="@*android:dimen/car_button_radius"/>
+ <solid android:color="@*android:color/car_grey_300"/>
+ </shape>
+ </item>
<item>
<ripple android:color="?android:attr/colorControlHighlight">
<item>
diff --git a/core/res/res/drawable-car/car_switch_track.xml b/core/res/res/drawable-car/car_switch_track.xml
index cb0b9be..51e9f7e 100644
--- a/core/res/res/drawable-car/car_switch_track.xml
+++ b/core/res/res/drawable-car/car_switch_track.xml
@@ -41,7 +41,7 @@
android:right="@dimen/car_switch_track_margin_size">
<shape
android:shape="rectangle"
- android:tint="@color/switch_track_material">
+ android:tint="@color/car_switch_track">
<corners android:radius="7dp" />
<solid android:color="@color/white_disabled_material" />
<size android:height="14dp" />
diff --git a/core/res/res/values/attrs_car.xml b/core/res/res/values/attrs_car.xml
new file mode 100644
index 0000000..6bfea97
--- /dev/null
+++ b/core/res/res/values/attrs_car.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+ the documentation output. To suppress comment lines from the documentation
+ output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+ <attr name="state_ux_restricted" format="boolean"/>
+</resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bb826de..f31233b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -147,6 +147,9 @@
<!-- WindowMetricsTest permissions -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <!-- WindowContextTest permissions -->
+ <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+
<!-- Allow use of PendingIntent.getIntent() -->
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index dcf5e02..da7304e 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -18,29 +18,38 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.content.Intent;
import android.hardware.display.DisplayManager;
+import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerImpl;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for {@link WindowContext}
*
@@ -54,20 +63,14 @@
@SmallTest
@Presubmit
public class WindowContextTest {
+ @Rule
+ public ActivityTestRule<EmptyActivity> mActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
+ false /* launchActivity */);
+
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private final WindowContext mWindowContext = createWindowContext();
-
- @Test
- public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
- final IBinder token = mWindowContext.getWindowContextToken();
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
- assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
-
- mWindowContext.release();
-
- assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
- }
+ private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
@Test
public void testCreateWindowContextWindowManagerAttachClientToken() {
@@ -83,12 +86,133 @@
assertEquals(mWindowContext.getWindowContextToken(), params.mWindowContextToken);
}
+ /**
+ * Test the {@link WindowContext} life cycle behavior to add a new window token:
+ * <ul>
+ * <li>The window token is created before adding the first view.</li>
+ * <li>The window token is registered after adding the first view.</li>
+ * <li>The window token is removed after {@link WindowContext}'s release.</li>
+ * </ul>
+ */
+ @Test
+ public void testCreateWindowContextNewTokenFromClient() throws Throwable {
+ final IBinder token = mWindowContext.getWindowContextToken();
+
+ // Test that the window token is not created yet.
+ assertFalse("Token must not be registered until adding the first window",
+ mWms.isWindowToken(token));
+
+ final WindowManager.LayoutParams params =
+ new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ final View testView = new View(mWindowContext);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ testView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {}
+ });
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+ assertEquals(token, params.mWindowContextToken);
+ });
+
+
+ assertTrue(latch.await(4, TimeUnit.SECONDS));
+
+
+ // Verify that the window token of the window context is created after first addView().
+ assertTrue("Token must exist after adding the first view.",
+ mWms.isWindowToken(token));
+
+ mWindowContext.release();
+
+ // After the window context's release, the window token is also removed.
+ assertFalse("Token must be removed after release.", mWms.isWindowToken(token));
+ }
+
+ /**
+ * Verifies the behavior when window context attaches an {@link Activity} by override
+ * {@link WindowManager.LayoutParams#token}.
+ *
+ * The window context token should be overridden to
+ * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must
+ * not be removed regardless of the release of window context.
+ */
+ @Test
+ public void testCreateWindowContext_AttachActivity_TokenNotRemovedAfterRelease()
+ throws Throwable {
+ mActivityRule.launchActivity(new Intent());
+ final Activity activity = mActivityRule.getActivity();
+ final WindowManager.LayoutParams params = activity.getWindow().getAttributes();
+
+ final WindowContext windowContext = createWindowContext(params.type);
+ final IBinder token = windowContext.getWindowContextToken();
+
+ final View testView = new View(windowContext);
+
+ mInstrumentation.runOnMainSync(() -> {
+ windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+ assertEquals(token, params.mWindowContextToken);
+ });
+ windowContext.release();
+
+ // Even if the window context is released, the activity should still exist.
+ assertTrue("Token must exist even if the window context is released.",
+ mWms.isWindowToken(activity.getActivityToken()));
+ }
+
+ /**
+ * Verifies the behavior when window context attaches an existing token by override
+ * {@link WindowManager.LayoutParams#token}.
+ *
+ * The window context token should be overridden to
+ * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must not be
+ * removed regardless of release of window context.
+ */
+ @Test
+ public void testCreateWindowContext_AttachWindowToken_TokenNotRemovedAfterRelease()
+ throws Throwable {
+ final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD);
+ final IBinder token = windowContext.getWindowContextToken();
+
+ final IBinder existingToken = new Binder();
+ mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId());
+
+ final WindowManager.LayoutParams params =
+ new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
+ params.token = existingToken;
+ final View testView = new View(windowContext);
+
+ mInstrumentation.runOnMainSync(() -> {
+ windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+ assertEquals(token, params.mWindowContextToken);
+ });
+ windowContext.release();
+
+ // Even if the window context is released, the existing token should still exist.
+ assertTrue("Token must exist even if the window context is released.",
+ mWms.isWindowToken(existingToken));
+
+ mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY);
+ }
+
private WindowContext createWindowContext() {
+ return createWindowContext(TYPE_APPLICATION_OVERLAY);
+ }
+
+ private WindowContext createWindowContext(@WindowType int type) {
final Context instContext = mInstrumentation.getTargetContext();
final Display display = instContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
- final Context context = instContext.createDisplayContext(display);
- return new WindowContext(context, TYPE_APPLICATION_OVERLAY,
- null /* options */);
+ return (WindowContext) instContext.createWindowContext(display, type, null /* options */);
}
}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35..0000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
- private static final int APP_WIDTH = 272;
- private static final int APP_HEIGHT = 700;
- // Tablet size device, ROTATION_0 corresponds to portrait.
- private static final int LOGICAL_WIDTH = 700;
- private static final int LOGICAL_HEIGHT = 1800;
-
- // Bounds of the app when the device is in portrait mode.
- private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
- private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
- private StaticMockitoSession mMockitoSession;
-
- private DisplayManagerGlobal mDisplayManagerGlobal;
- private Context mApplicationContext;
- private DisplayInfo mDisplayInfo = new DisplayInfo();
-
- @Before
- public void setupTests() {
- mMockitoSession = mockitoSession()
- .mockStatic(DisplayManagerGlobal.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
-
- // Ensure no adjustments are set before each test.
- mApplicationContext = ApplicationProvider.getApplicationContext();
- DisplayAdjustments displayAdjustments =
- mApplicationContext.getResources().getDisplayAdjustments();
- displayAdjustments.setFixedRotationAdjustments(null);
- mApplicationContext.getResources().overrideDisplayAdjustments(null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
- null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
- null);
- mDisplayInfo.rotation = ROTATION_0;
-
- mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
- doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
- }
-
- @After
- public void teardownTests() {
- if (mMockitoSession != null) {
- mMockitoSession.finishMocking();
- }
- Mockito.framework().clearInlineMocks();
- }
-
- @Test
- public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testConstructor_defaultResources_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- mApplicationContext.getResources().getDisplayAdjustments());
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- // GIVEN display is constructed with display adjustments.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- displayAdjustments);
- // THEN rotation is not adjusted since no override was set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is not adjusted since no override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is adjusted since an override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_90);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated with an override.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
- }
-
- // Given rotated display dimensions, calculate the letterboxed app bounds.
- private static Rect buildAppBounds(int displayWidth, int displayHeight) {
- final int midWidth = displayWidth / 2;
- final int left = midWidth - (APP_WIDTH / 2);
- final int right = midWidth + (APP_WIDTH / 2);
- final int midHeight = displayHeight / 2;
- // Coordinate system starts at top left.
- final int top = midHeight - (APP_HEIGHT / 2);
- final int bottom = midHeight + (APP_HEIGHT / 2);
- return new Rect(left, top, right, bottom);
- }
-
- private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_90;
- // Flip width & height assignment since the device is rotated.
- displayInfo.logicalWidth = LOGICAL_HEIGHT;
- displayInfo.logicalHeight = LOGICAL_WIDTH;
- }
-
- private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_0;
- displayInfo.logicalWidth = LOGICAL_WIDTH;
- displayInfo.logicalHeight = LOGICAL_HEIGHT;
- }
-
- /**
- * Set max bounds to be sandboxed to the app bounds, indicating the app is in
- * size compat mode or letterbox.
- */
- private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
- resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
- }
-
- /**
- * Do not compare entire display info, since it is updated to match display the test is run on.
- */
- private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
- assertThat(actual.displayId).isEqualTo(expected.displayId);
- assertThat(actual.rotation).isEqualTo(expected.rotation);
- assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
- assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeIsLandscape(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- // Flip the width and height check since the device is rotated.
- assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
- }
-
- private static void verifyRealMetricsIsLandscape(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- // Flip the width and height check since the device is rotated.
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
- }
-
- private static void verifyRealSizeIsPortrait(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
- }
-
- private static void verifyRealMetricsIsPortrait(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
- }
-
- private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
- assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
- }
-
- private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
- Resources resources, @Surface.Rotation int rotation) {
- FixedRotationAdjustments fixedRotationAdjustments =
- setFixedRotationAdjustments(resources, rotation);
- resources.overrideDisplayAdjustments(
- buildOverrideRotationAdjustments(fixedRotationAdjustments));
- return fixedRotationAdjustments;
- }
-
- private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
- @Surface.Rotation int rotation) {
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
- return fixedRotationAdjustments;
- }
-
- private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
- FixedRotationAdjustments fixedRotationAdjustments) {
- return consumedDisplayAdjustments
- -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- }
-}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296..222c9bd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "123161180": {
+ "message": "SEVER CHILDREN",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2137,12 +2143,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "332390227": {
- "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2623,12 +2623,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
- "910200295": {
- "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"913494177": {
"message": "removeAllWindowsIfPossible: removing win=%s",
"level": "WARN",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index a7d3f798..5b79d76 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -175,6 +175,39 @@
public static final int Y16 = 0x20363159;
/**
+ * <p>Android YUV P010 format.</p>
+ *
+ * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+ * followed immediately by a Wx(H/2) CbCr plane. Each sample is
+ * represented by a 16-bit little-endian value, with the lower 6 bits set
+ * to zero.
+ *
+ * <p>This format assumes
+ * <ul>
+ * <li>an even height</li>
+ * <li>a vertical stride equal to the height</li>
+ * </ul>
+ * </p>
+ *
+ * <pre> stride_in_bytes = stride * 2 </pre>
+ * <pre> y_size = stride_in_bytes * height </pre>
+ * <pre> cbcr_size = stride_in_bytes * (height / 2) </pre>
+ * <pre> cb_offset = y_size </pre>
+ * <pre> cr_offset = cb_offset + 2 </pre>
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.camera2.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ *
+ */
+ public static final int YCBCR_P010 = 0x36;
+
+ /**
* YCbCr format, used for video.
*
* <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is
@@ -807,6 +840,8 @@
case RAW_DEPTH:
case RAW_SENSOR:
return 16;
+ case YCBCR_P010:
+ return 20;
case RAW_DEPTH10:
case RAW10:
return 10;
@@ -839,6 +874,7 @@
case YUV_420_888:
case YUV_422_888:
case YUV_444_888:
+ case YCBCR_P010:
case FLEX_RGB_888:
case FLEX_RGBA_8888:
case RAW_SENSOR:
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 005a726..35e6b859 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.LruCache;
@@ -67,7 +68,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -147,7 +147,7 @@
*/
@GuardedBy("SYSTEM_FONT_MAP_LOCK")
@UnsupportedAppUsage(trackingBug = 123769347)
- static final Map<String, Typeface> sSystemFontMap = new HashMap<>();
+ static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>();
// DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
static ByteBuffer sSystemFontMapBuffer = null;
@@ -1231,7 +1231,7 @@
/** @hide */
@VisibleForTesting
public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
- Map<String, Typeface> fontMap = new HashMap<>();
+ Map<String, Typeface> fontMap = new ArrayMap<>();
int typefacesBytesCount = buffer.getInt();
long[] nativePtrs = nativeReadTypefaces(buffer.slice());
if (nativePtrs == null) {
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index fea756c..9214ff1 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -486,7 +486,8 @@
long ptr;
int fontIdentifier;
if (mFont == null) {
- ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic, mTtcIndex);
+ ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
+ mTtcIndex);
long fontBufferPtr = nGetFontBufferAddress(ptr);
synchronized (SOURCE_ID_LOCK) {
long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
@@ -513,8 +514,8 @@
@CriticalNative
private static native void nAddAxis(long builderPtr, int tag, float value);
private static native long nBuild(
- long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath, int weight,
- boolean italic, int ttcIndex);
+ long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath,
+ @NonNull String localeList, int weight, boolean italic, int ttcIndex);
@CriticalNative
private static native long nGetReleaseNativeFont();
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c29c194..77f86fe 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -20,15 +20,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.text.FontConfig;
+import android.util.SparseIntArray;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
import java.util.ArrayList;
-import java.util.HashSet;
/**
* A font family class can be used for creating Typeface.
@@ -68,7 +69,9 @@
nGetReleaseNativeFamily());
private final ArrayList<Font> mFonts = new ArrayList<>();
- private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+ // Most FontFamily only has regular, bold, italic, bold-italic. Thus 4 should be good for
+ // initial capacity.
+ private final SparseIntArray mStyles = new SparseIntArray(4);
/**
* Constructs a builder.
@@ -77,7 +80,7 @@
*/
public Builder(@NonNull Font font) {
Preconditions.checkNotNull(font, "font can not be null");
- mStyleHashSet.add(makeStyleIdentifier(font));
+ mStyles.append(makeStyleIdentifier(font), 0);
mFonts.add(font);
}
@@ -97,9 +100,11 @@
*/
public @NonNull Builder addFont(@NonNull Font font) {
Preconditions.checkNotNull(font, "font can not be null");
- if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+ int key = makeStyleIdentifier(font);
+ if (mStyles.indexOfKey(key) >= 0) {
throw new IllegalArgumentException(font + " has already been added");
}
+ mStyles.append(key, 0);
mFonts.add(font);
return this;
}
@@ -120,7 +125,7 @@
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
- final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr);
+ final FontFamily family = new FontFamily(mFonts, ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
}
@@ -139,15 +144,11 @@
}
private final ArrayList<Font> mFonts;
- private final String mLangTags;
- private final int mVariant;
private final long mNativePtr;
// Use Builder instead.
- private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) {
+ private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
mFonts = fonts;
- mLangTags = langTags;
- mVariant = variant;
mNativePtr = ptr;
}
@@ -157,7 +158,7 @@
* @return a BCP-47 compliant language tag.
*/
public @Nullable String getLangTags() {
- return mLangTags;
+ return nGetLangTags(mNativePtr);
}
/**
@@ -165,7 +166,7 @@
* @return a family variant
*/
public int getVariant() {
- return mVariant;
+ return nGetVariant(mNativePtr);
}
/**
@@ -191,4 +192,10 @@
public long getNativePtr() {
return mNativePtr;
}
+
+ @FastNative
+ private static native String nGetLangTags(long family);
+
+ @CriticalNative
+ private static native int nGetVariant(long family);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index c166e12..904085f 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,6 +22,7 @@
import android.graphics.Typeface;
import android.text.FontConfig;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -36,8 +37,6 @@
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -76,7 +75,7 @@
if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
sAvailableFonts = collectAllFonts();
} else {
- Set<Font> set = new HashSet<>();
+ Set<Font> set = new ArraySet<>();
for (FontFamily[] items : sFamilyMap.values()) {
for (FontFamily family : items) {
for (int i = 0; i < family.getSize(); ++i) {
@@ -96,7 +95,7 @@
FontConfig fontConfig = getSystemPreinstalledFontConfig();
Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
- Set<Font> res = new HashSet<>();
+ Set<Font> res = new ArraySet<>();
for (FontFamily[] families : map.values()) {
for (FontFamily family : families) {
for (int i = 0; i < family.getSize(); ++i) {
@@ -218,7 +217,7 @@
}
private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily,
- @NonNull HashMap<String, ByteBuffer> bufferCache,
+ @NonNull ArrayMap<String, ByteBuffer> bufferCache,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
final String familyName = xmlFamily.getName();
final FontFamily family = createFontFamily(
@@ -284,8 +283,8 @@
*/
@VisibleForTesting
public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
- final Map<String, FontFamily[]> fallbackMap = new HashMap<>();
- final HashMap<String, ByteBuffer> bufferCache = new HashMap<>();
+ final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -326,7 +325,7 @@
public static Map<String, Typeface> buildSystemTypefaces(
FontConfig fontConfig,
Map<String, FontFamily[]> fallbackMap) {
- final HashMap<String, Typeface> result = new HashMap<>();
+ final ArrayMap<String, Typeface> result = new ArrayMap<>();
Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
return result;
}
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 7fbbb61..4612ba2 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Sidecar
android_library_import {
name: "window-sidecar",
aars: ["window-sidecar-release.aar"],
@@ -20,7 +21,7 @@
java_library {
name: "androidx.window.sidecar",
- srcs: ["src/**/*.java"],
+ srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
static_libs: ["window-sidecar"],
installable: true,
sdk_version: "core_platform",
@@ -36,3 +37,31 @@
src: "androidx.window.sidecar.xml",
filename_from_src: true,
}
+
+// Extensions
+// NOTE: This module is still under active development and must not
+// be used in production. Use 'androidx.window.sidecar' instead.
+android_library_import {
+ name: "window-extensions",
+ aars: ["window-extensions-release.aar"],
+ sdk_version: "current",
+}
+
+java_library {
+ name: "androidx.window.extensions",
+ srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
+ static_libs: ["window-extensions"],
+ installable: true,
+ sdk_version: "core_platform",
+ system_ext_specific: true,
+ libs: ["framework", "androidx.annotation_annotation",],
+ required: ["androidx.window.extensions.xml",],
+}
+
+prebuilt_etc {
+ name: "androidx.window.extensions.xml",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "androidx.window.extensions.xml",
+ filename_from_src: true,
+}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
new file mode 100644
index 0000000..34264aa
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<permissions>
+ <library
+ name="androidx.window.extensions"
+ file="/system_ext/framework/androidx.window.extensions.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
new file mode 100644
index 0000000..b7a6039
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.content.Context;
+
+/**
+ * Provider class that will instantiate the library implementation. It must be included in the
+ * vendor library, and the vendor implementation must match the signature of this class.
+ */
+public class ExtensionProvider {
+ /**
+ * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
+ * an OEM by overriding this method.
+ */
+ public static ExtensionInterface getExtensionImpl(Context context) {
+ return new SampleExtensionImpl(context);
+ }
+
+ /**
+ * The support library will use this method to check API version compatibility.
+ * @return API version string in MAJOR.MINOR.PATCH-description format.
+ */
+ public static String getApiVersion() {
+ return "1.0.0-settings_sample";
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
new file mode 100644
index 0000000..0bf6965
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.extensions OEM interface for use with
+ * WindowManager Jetpack.
+ *
+ * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
+ * production builds since the interface can still change before reaching stable version.
+ * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
+ */
+class SampleExtensionImpl extends StubExtension implements
+ SettingsConfigProvider.StateChangeCallback {
+ private static final String TAG = "SampleExtension";
+
+ private final SettingsConfigProvider mConfigProvider;
+
+ SampleExtensionImpl(Context context) {
+ mConfigProvider = new SettingsConfigProvider(context, this);
+ }
+
+ @Override
+ public void onDevicePostureChanged() {
+ updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+ }
+
+ @Override
+ public void onDisplayFeaturesChanged() {
+ for (Activity activity : getActivitiesListeningForLayoutChanges()) {
+ ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+ updateWindowLayout(activity, newLayout);
+ }
+ }
+
+ @NonNull
+ private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+ List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
+ return new ExtensionWindowLayoutInfo(displayFeatures);
+ }
+
+ private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<ExtensionDisplayFeature> features = new ArrayList<>();
+ int displayId = activity.getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ return features;
+ }
+
+ if (activity.isInMultiWindowMode()) {
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ return features;
+ }
+
+ List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+ for (BaseDisplayFeature baseFeature : storedFeatures) {
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+ features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+ baseFeature.getState()));
+ }
+ return features;
+ }
+
+ @Override
+ protected void onListenersChanged() {
+ if (hasListeners()) {
+ mConfigProvider.registerObserversIfNeeded();
+ } else {
+ mConfigProvider.unregisterObserversIfNeeded();
+ }
+
+ onDevicePostureChanged();
+ onDisplayFeaturesChanged();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
new file mode 100644
index 0000000..b0895ef
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubExtension implements ExtensionInterface {
+
+ private ExtensionCallback mExtensionCallback;
+ private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
+ private boolean mDeviceStateChangeListenerRegistered;
+
+ StubExtension() {
+ }
+
+ @Override
+ public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
+ this.mExtensionCallback = extensionCallback;
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+ this.mWindowLayoutChangeListenerActivities.add(activity);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+ this.mWindowLayoutChangeListenerActivities.remove(activity);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onDeviceStateListenersChanged(boolean isEmpty) {
+ this.mDeviceStateChangeListenerRegistered = !isEmpty;
+ this.onListenersChanged();
+ }
+
+ void updateDeviceState(ExtensionDeviceState newState) {
+ if (this.mExtensionCallback != null) {
+ mExtensionCallback.onDeviceStateChanged(newState);
+ }
+ }
+
+ void updateWindowLayout(@NonNull Activity activity,
+ @NonNull ExtensionWindowLayoutInfo newLayout) {
+ if (this.mExtensionCallback != null) {
+ mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
+ }
+ }
+
+ @NonNull
+ Set<Activity> getActivitiesListeningForLayoutChanges() {
+ return mWindowLayoutChangeListenerActivities;
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListenerActivities.isEmpty()
+ || mDeviceStateChangeListenerRegistered;
+ }
+
+ protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
new file mode 100644
index 0000000..1094a0e
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.sidecar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.sidecar OEM interface for use with
+ * WindowManager Jetpack.
+ */
+class SampleSidecarImpl extends StubSidecar implements
+ SettingsConfigProvider.StateChangeCallback {
+ private static final String TAG = "SampleSidecar";
+
+ private final SettingsConfigProvider mConfigProvider;
+
+ SampleSidecarImpl(Context context) {
+ mConfigProvider = new SettingsConfigProvider(context, this);
+ }
+
+ @Override
+ public void onDevicePostureChanged() {
+ updateDeviceState(getDeviceState());
+ }
+
+ @Override
+ public void onDisplayFeaturesChanged() {
+ for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
+ SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+ updateWindowLayout(windowToken, newLayout);
+ }
+ }
+
+ @NonNull
+ @Override
+ public SidecarDeviceState getDeviceState() {
+ SidecarDeviceState deviceState = new SidecarDeviceState();
+ deviceState.posture = mConfigProvider.getDeviceState();
+ return deviceState;
+ }
+
+ @NonNull
+ @Override
+ public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+ Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
+ SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+ if (activity == null) {
+ return windowLayoutInfo;
+ }
+ windowLayoutInfo.displayFeatures = getDisplayFeatures(activity);
+ return windowLayoutInfo;
+ }
+
+ private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
+ int displayId = activity.getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ return features;
+ }
+
+ if (activity.isInMultiWindowMode()) {
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ return features;
+ }
+
+ List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+ for (BaseDisplayFeature baseFeature : storedFeatures) {
+ SidecarDisplayFeature feature = new SidecarDisplayFeature();
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+ feature.setRect(featureRect);
+ feature.setType(baseFeature.getType());
+ features.add(feature);
+ }
+ return features;
+ }
+
+ @Override
+ protected void onListenersChanged() {
+ if (hasListeners()) {
+ mConfigProvider.registerObserversIfNeeded();
+ } else {
+ mConfigProvider.unregisterObserversIfNeeded();
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
deleted file mode 100644
index 5397302..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.sidecar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
-import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
-import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
-import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class SettingsSidecarImpl extends StubSidecar {
- private static final String TAG = "SettingsSidecar";
-
- private static final String DEVICE_POSTURE = "device_posture";
- private static final String DISPLAY_FEATURES = "display_features";
-
- private static final Pattern FEATURE_PATTERN =
- Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
-
- private static final String FEATURE_TYPE_FOLD = "fold";
- private static final String FEATURE_TYPE_HINGE = "hinge";
-
- private Context mContext;
- private SettingsObserver mSettingsObserver;
-
- final class SettingsObserver extends ContentObserver {
- private final Uri mDevicePostureUri =
- Settings.Global.getUriFor(DEVICE_POSTURE);
- private final Uri mDisplayFeaturesUri =
- Settings.Global.getUriFor(DISPLAY_FEATURES);
- private final ContentResolver mResolver = mContext.getContentResolver();
- private boolean mRegisteredObservers;
-
-
- private SettingsObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- private void registerObserversIfNeeded() {
- if (mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = true;
- mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */,
- this /* ContentObserver */);
- mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */,
- this /* ContentObserver */);
- }
-
- private void unregisterObserversIfNeeded() {
- if (!mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = false;
- mResolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (uri == null) {
- return;
- }
-
- if (mDevicePostureUri.equals(uri)) {
- updateDevicePosture();
- return;
- }
- if (mDisplayFeaturesUri.equals(uri)) {
- updateDisplayFeatures();
- return;
- }
- }
- }
-
- SettingsSidecarImpl(Context context) {
- mContext = context;
- mSettingsObserver = new SettingsObserver();
- }
-
- private void updateDevicePosture() {
- updateDeviceState(getDeviceState());
- }
-
- /** Update display features with values read from settings. */
- private void updateDisplayFeatures() {
- for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
- SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
- updateWindowLayout(windowToken, newLayout);
- }
- }
-
- @NonNull
- @Override
- public SidecarDeviceState getDeviceState() {
- ContentResolver resolver = mContext.getContentResolver();
- int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
- SidecarDeviceState.POSTURE_UNKNOWN);
- SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture;
- return deviceState;
- }
-
- @NonNull
- @Override
- public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
- List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
- SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
- windowLayoutInfo.displayFeatures = displayFeatures;
- return windowLayoutInfo;
- }
-
- private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
- List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
- int displayId = getWindowDisplay(windowToken);
- if (displayId != DEFAULT_DISPLAY) {
- Log.w(TAG, "This sample doesn't support display features on secondary displays");
- return features;
- }
-
- if (isInMultiWindow(windowToken)) {
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return features;
- }
-
- ContentResolver resolver = mContext.getContentResolver();
- String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES);
- if (TextUtils.isEmpty(displayFeaturesString)) {
- displayFeaturesString = mContext.getResources().getString(
- R.string.config_display_features);
- }
- if (TextUtils.isEmpty(displayFeaturesString)) {
- return features;
- }
-
- String[] featureStrings = displayFeaturesString.split(";");
- for (String featureString : featureStrings) {
- Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
- if (!featureMatcher.matches()) {
- Log.e(TAG, "Malformed feature description format: " + featureString);
- continue;
- }
- try {
- String featureType = featureMatcher.group(1);
- int type;
- switch (featureType) {
- case FEATURE_TYPE_FOLD:
- type = SidecarDisplayFeature.TYPE_FOLD;
- break;
- case FEATURE_TYPE_HINGE:
- type = SidecarDisplayFeature.TYPE_HINGE;
- break;
- default: {
- Log.e(TAG, "Malformed feature type: " + featureType);
- continue;
- }
- }
-
- int left = Integer.parseInt(featureMatcher.group(2));
- int top = Integer.parseInt(featureMatcher.group(3));
- int right = Integer.parseInt(featureMatcher.group(4));
- int bottom = Integer.parseInt(featureMatcher.group(5));
- Rect featureRect = new Rect(left, top, right, bottom);
- rotateRectToDisplayRotation(featureRect, displayId);
- transformToWindowSpaceRect(featureRect, windowToken);
- if (isNotZero(featureRect)) {
- SidecarDisplayFeature feature = new SidecarDisplayFeature();
- feature.setRect(featureRect);
- feature.setType(type);
- features.add(feature);
- } else {
- Log.w(TAG, "Failed to adjust feature to window");
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Malformed feature description: " + featureString);
- }
- }
- return features;
- }
-
- private static boolean isNotZero(Rect rect) {
- return rect.height() > 0 || rect.width() > 0;
- }
-
- @Override
- protected void onListenersChanged() {
- if (mSettingsObserver == null) {
- return;
- }
-
- if (hasListeners()) {
- mSettingsObserver.registerObserversIfNeeded();
- } else {
- mSettingsObserver.unregisterObserversIfNeeded();
- }
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 0b4915ed..e6f8388 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -28,7 +28,7 @@
* an OEM by overriding this method.
*/
public static SidecarInterface getSidecarImpl(Context context) {
- return new SettingsSidecarImpl(context);
+ return new SampleSidecarImpl(context);
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
new file mode 100644
index 0000000..b74a2a4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.util;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+public class BaseDisplayFeature {
+ private final int mType;
+ private final int mState;
+ @NonNull
+ public final Rect mRect;
+
+ public BaseDisplayFeature(int type, int state, @NonNull Rect rect) {
+ this.mType = type;
+ this.mState = state;
+ if (rect.width() == 0 && rect.height() == 0) {
+ throw new IllegalArgumentException(
+ "Display feature rectangle cannot have zero width and height simultaneously.");
+ }
+ this.mRect = rect;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getState() {
+ return mState;
+ }
+
+ @NonNull
+ public Rect getRect() {
+ return mRect;
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
similarity index 62%
rename from libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index e5b6cff..2a593f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,30 +14,36 @@
* limitations under the License.
*/
-package androidx.window.sidecar;
+package androidx.window.util;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.app.Activity;
-import android.app.ActivityThread;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
import android.view.DisplayInfo;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-class SidecarHelper {
+/**
+ * Util class for both Sidecar and Extensions.
+ */
+public final class ExtensionHelper {
+
+ private ExtensionHelper() {
+ // Util class, no instances should be created.
+ }
+
/**
- * Rotate the input rectangle specified in default display orientation to the current display
+ * Rotates the input rectangle specified in default display orientation to the current display
* rotation.
*/
- static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
+ public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
int rotation = displayInfo.rotation;
@@ -52,7 +58,7 @@
}
/**
- * Rotate the input rectangle within parent bounds for a given delta.
+ * Rotates the input rectangle within parent bounds for a given delta.
*/
private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
@Surface.Rotation int delta) {
@@ -79,9 +85,9 @@
}
}
- /** Transform rectangle from absolute coordinate space to the window coordinate space. */
- static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
- Rect windowRect = getWindowBounds(windowToken);
+ /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
+ public static void transformToWindowSpaceRect(Activity activity, Rect inOutRect) {
+ Rect windowRect = getWindowBounds(activity);
if (windowRect == null) {
inOutRect.setEmpty();
return;
@@ -95,32 +101,17 @@
}
/**
- * Get the current window bounds in absolute coordinates.
- * NOTE: Only works with Activity windows.
+ * Gets the current window bounds in absolute coordinates.
*/
@Nullable
- private static Rect getWindowBounds(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null
- ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
- : null;
+ private static Rect getWindowBounds(@NonNull Activity activity) {
+ return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
}
/**
- * Check if this window is an Activity window that is in multi-window mode.
+ * Checks if both dimensions of the given rect are zero at the same time.
*/
- static boolean isInMultiWindow(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null && activity.isInMultiWindowMode();
- }
-
- /**
- * Get the id of the parent display for the window.
- * NOTE: Only works with Activity windows.
- */
- static int getWindowDisplay(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null
- ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY;
+ public static boolean isZero(@NonNull Rect rect) {
+ return rect.height() == 0 && rect.width() == 0;
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
new file mode 100644
index 0000000..6dd190c
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.util;
+
+import static androidx.window.util.ExtensionHelper.isZero;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Device and display feature state provider that uses Settings as the source.
+ */
+public final class SettingsConfigProvider extends ContentObserver {
+ private static final String TAG = "SettingsConfigProvider";
+ private static final String DEVICE_POSTURE = "device_posture";
+ private static final String DISPLAY_FEATURES = "display_features";
+
+ private static final Pattern FEATURE_PATTERN =
+ Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+
+ private static final String FEATURE_TYPE_FOLD = "fold";
+ private static final String FEATURE_TYPE_HINGE = "hinge";
+
+ private final Uri mDevicePostureUri =
+ Settings.Global.getUriFor(DEVICE_POSTURE);
+ private final Uri mDisplayFeaturesUri =
+ Settings.Global.getUriFor(DISPLAY_FEATURES);
+ private final Context mContext;
+ private final ContentResolver mResolver;
+ private final StateChangeCallback mCallback;
+ private boolean mRegisteredObservers;
+
+ public SettingsConfigProvider(@NonNull Context context, @NonNull StateChangeCallback callback) {
+ super(new Handler(Looper.getMainLooper()));
+ mContext = context;
+ mResolver = context.getContentResolver();
+ mCallback = callback;
+ }
+
+ /**
+ * Registers the content observers for Settings keys that store device state and display feature
+ * configurations.
+ */
+ public void registerObserversIfNeeded() {
+ if (mRegisteredObservers) {
+ return;
+ }
+ mRegisteredObservers = true;
+ mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
+ this /* ContentObserver */);
+ mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
+ this /* ContentObserver */);
+ }
+
+ /**
+ * Unregisters the content observers that are tracking the state changes.
+ * @see #registerObserversIfNeeded()
+ */
+ public void unregisterObserversIfNeeded() {
+ if (!mRegisteredObservers) {
+ return;
+ }
+ mRegisteredObservers = false;
+ mResolver.unregisterContentObserver(this);
+ }
+
+ /**
+ * Gets the device posture int stored in Settings.
+ */
+ public int getDeviceState() {
+ return Settings.Global.getInt(mResolver, DEVICE_POSTURE,
+ 0 /* POSTURE_UNKNOWN */);
+ }
+
+ /**
+ * Gets the list of all display feature configs stored in Settings. Uses a custom
+ * {@link BaseDisplayFeature} class to report the config to be translated for actual
+ * containers in Sidecar or Extensions.
+ */
+ public List<BaseDisplayFeature> getDisplayFeatures() {
+ List<BaseDisplayFeature> features = new ArrayList<>();
+ String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ displayFeaturesString = mContext.getResources().getString(
+ R.string.config_display_features);
+ }
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ return features;
+ }
+ String[] featureStrings = displayFeaturesString.split(";");
+
+ int deviceState = getDeviceState();
+
+ for (String featureString : featureStrings) {
+ Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
+ if (!featureMatcher.matches()) {
+ Log.e(TAG, "Malformed feature description format: " + featureString);
+ continue;
+ }
+ try {
+ String featureType = featureMatcher.group(1);
+ int type;
+ switch (featureType) {
+ case FEATURE_TYPE_FOLD:
+ type = 1 /* TYPE_FOLD */;
+ break;
+ case FEATURE_TYPE_HINGE:
+ type = 2 /* TYPE_HINGE */;
+ break;
+ default: {
+ Log.e(TAG, "Malformed feature type: " + featureType);
+ continue;
+ }
+ }
+
+ int left = Integer.parseInt(featureMatcher.group(2));
+ int top = Integer.parseInt(featureMatcher.group(3));
+ int right = Integer.parseInt(featureMatcher.group(4));
+ int bottom = Integer.parseInt(featureMatcher.group(5));
+ Rect featureRect = new Rect(left, top, right, bottom);
+ if (!isZero(featureRect)) {
+ BaseDisplayFeature feature = new BaseDisplayFeature(type, deviceState,
+ featureRect);
+ features.add(feature);
+ } else {
+ Log.w(TAG, "Read empty feature");
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Malformed feature description: " + featureString);
+ }
+ }
+ return features;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri == null) {
+ return;
+ }
+
+ if (mDevicePostureUri.equals(uri)) {
+ mCallback.onDevicePostureChanged();
+ mCallback.onDisplayFeaturesChanged();
+ return;
+ }
+ if (mDisplayFeaturesUri.equals(uri)) {
+ mCallback.onDisplayFeaturesChanged();
+ }
+ }
+
+ /**
+ * Callback that notifies about device or display feature state changes.
+ */
+ public interface StateChangeCallback {
+ /**
+ * Notifies about the device state update.
+ */
+ void onDevicePostureChanged();
+
+ /**
+ * Notifies about the display feature config update.
+ */
+ void onDisplayFeaturesChanged();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
new file mode 100644
index 0000000..7b306b0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689..255e4d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.animation
-import android.os.Looper
import android.util.ArrayMap
import android.util.Log
import android.view.View
@@ -847,7 +846,7 @@
* pass to [spring].
*/
data class SpringConfig internal constructor(
- internal var stiffness: Float,
+ var stiffness: Float,
internal var dampingRatio: Float,
internal var startVelocity: Float = 0f,
internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@
*/
data class FlingConfig internal constructor(
internal var friction: Float,
- internal var min: Float,
- internal var max: Float,
+ var min: Float,
+ var max: Float,
internal var startVelocity: Float
) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 39510e5..4e19e06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1478,6 +1478,9 @@
* Update bubble order and pointer position.
*/
public void updateBubbleOrder(List<Bubble> bubbles) {
+ if (isExpansionAnimating()) {
+ return;
+ }
final Runnable reorder = () -> {
for (int i = 0; i < bubbles.size(); i++) {
Bubble bubble = bubbles.get(i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 8fb358a..b91ba34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -64,6 +64,7 @@
private static final String TAG = "PipResizeGestureHandler";
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
+ private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -539,6 +540,11 @@
// position correctly. Drag-resize does not need to move, so just finalize resize.
if (mUsingPinchToZoom) {
final Rect startBounds = new Rect(mLastResizeBounds);
+ // If user resize is pretty close to max size, just auto resize to max.
+ if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
+ || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
+ mLastResizeBounds.set(0, 0, mMaxSize.x, mMaxSize.y);
+ }
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca2732..c0ab20d 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
android_test {
name: "WMShellUnitTests",
- srcs: ["**/*.java"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
static_libs: [
"WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed..17ed396 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringForce
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.animation.PhysicsAnimator.EndListener
import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
private lateinit var viewGroup: ViewGroup
private lateinit var testView: View
private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5..dd1a6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe53641..9f1ee6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@
import android.view.View
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
/** Incrementing value for fake MotionEvent timestamps. */
private var time = 0L
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index b944310..b769d40 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -79,7 +79,8 @@
// Regular JNI
static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
- jstring filePath, jint weight, jboolean italic, jint ttcIndex) {
+ jstring filePath, jstring langTags, jint weight, jboolean italic,
+ jint ttcIndex) {
NPE_CHECK_RETURN_ZERO(env, buffer);
std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
const void* fontPtr = env->GetDirectBufferAddress(buffer);
@@ -94,6 +95,7 @@
return 0;
}
ScopedUtfChars fontPath(env, filePath);
+ ScopedUtfChars langTagStr(env, langTags);
jobject fontRef = MakeGlobalRefOrDie(env, buffer);
sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
release_global_ref, reinterpret_cast<void*>(fontRef)));
@@ -105,8 +107,13 @@
"Failed to create internal object. maybe invalid font data.");
return 0;
}
- std::shared_ptr<minikin::Font> font = minikin::Font::Builder(minikinFont).setWeight(weight)
- .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+ uint32_t localeListId = minikin::registerLocaleList(langTagStr.c_str());
+ std::shared_ptr<minikin::Font> font =
+ minikin::Font::Builder(minikinFont)
+ .setWeight(weight)
+ .setSlant(static_cast<minikin::FontStyle::Slant>(italic))
+ .setLocaleListId(localeListId)
+ .build();
return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
}
@@ -302,11 +309,12 @@
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontBuilderMethods[] = {
- { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
- { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
- { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
- { "nClone", "(JJIZI)J", (void*) Font_Builder_clone },
- { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
+ {"nInitBuilder", "()J", (void*)Font_Builder_initBuilder},
+ {"nAddAxis", "(JIF)V", (void*)Font_Builder_addAxis},
+ {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
+ (void*)Font_Builder_build},
+ {"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
+ {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
};
static const JNINativeMethod gFontMethods[] = {
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 37e5276..a07723f 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -83,6 +83,23 @@
return reinterpret_cast<jlong>(releaseFontFamily);
}
+// FastNative
+static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ uint32_t localeListId = family->family->localeListId();
+ if (localeListId == 0) {
+ return nullptr;
+ }
+ std::string langTags = minikin::getLocaleString(localeListId);
+ return env->NewStringUTF(langTags.c_str());
+}
+
+// CriticalNative
+static jint FontFamily_getVariant(jlong familyPtr) {
+ FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+ return static_cast<jint>(family->family->variant());
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
@@ -93,9 +110,16 @@
{ "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
};
+static const JNINativeMethod gFontFamilyMethods[] = {
+ {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
+ {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
+};
+
int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
- gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+ gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods,
+ NELEM(gFontFamilyMethods));
}
}
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index d248f61..7837d7e 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -45,6 +45,7 @@
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
+ case ImageFormat.YCBCR_P010:
return 3;
case ImageFormat.NV16:
return 2;
@@ -225,6 +226,7 @@
case ImageFormat.RAW_SENSOR:
case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
case ImageFormat.DEPTH16:
+ case ImageFormat.YCBCR_P010:
estimatedBytePerPixel = 2.0;
break;
case PixelFormat.RGB_888:
@@ -244,6 +246,7 @@
private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
switch (image.getFormat()) {
+ case ImageFormat.YCBCR_P010:
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index b6c47fca..ecd9cc1 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -69,6 +69,7 @@
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
case HAL_PIXEL_FORMAT_BLOB:
case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ case HAL_PIXEL_FORMAT_YCBCR_P010:
return false;
case HAL_PIXEL_FORMAT_YV12:
@@ -261,6 +262,32 @@
pStride = 1;
rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
break;
+ case HAL_PIXEL_FORMAT_YCBCR_P010:
+ if (buffer->height % 2 != 0) {
+ ALOGE("YCBCR_P010: height (%d) should be a multiple of 2", buffer->height);
+ return BAD_VALUE;
+ }
+
+ if (buffer->width <= 0) {
+ ALOGE("YCBCR_P010: width (%d) should be a > 0", buffer->width);
+ return BAD_VALUE;
+ }
+
+ if (buffer->height <= 0) {
+ ALOGE("YCBCR_P010: height (%d) should be a > 0", buffer->height);
+ return BAD_VALUE;
+ }
+
+ ySize = (buffer->stride * 2) * buffer->height;
+ cSize = ySize / 2;
+ pStride = (idx == 0) ? 2 : 4;
+ cb = buffer->data + ySize;
+ cr = cb + 2;
+
+ pData = (idx == 0) ? buffer->data : (idx == 1) ? cb : cr;
+ dataSize = (idx == 0) ? ySize : cSize;
+ rStride = buffer->stride * 2;
+ break;
case HAL_PIXEL_FORMAT_Y8:
// Single plane, 8bpp.
LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 9ec84d9..eee9f1e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3786,7 +3786,7 @@
jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
return -1;
}
- int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
+ int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size);
env->ReleaseByteArrayElements(buffer, dst, 0);
return (jint) realReadSize;
}
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 1a2f8c0..748d458 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -88,12 +88,19 @@
}
sp<TimeFilterClient> DemuxClient::openTimeFilter() {
- // TODO: pending aidl interface
+ if (mTunerDemux != NULL) {
+ shared_ptr<ITunerTimeFilter> tunerTimeFilter;
+ Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return new TimeFilterClient(tunerTimeFilter);
+ }
if (mDemux != NULL) {
sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
if (hidlTimeFilter != NULL) {
- sp<TimeFilterClient> timeFilterClient = new TimeFilterClient();
+ sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL);
timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
return timeFilterClient;
}
@@ -103,7 +110,14 @@
}
int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ int hwId;
+ Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return INVALID_AV_SYNC_HW_ID;
+ }
+ return hwId;
+ }
if (mDemux != NULL) {
uint32_t avSyncHwId;
@@ -119,11 +133,18 @@
}
}
- return -1;
+ return INVALID_AV_SYNC_HW_ID;
}
long DemuxClient::getAvSyncTime(int avSyncHwId) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ int64_t time;
+ Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return INVALID_AV_SYNC_TIME;
+ }
+ return time;
+ }
if (mDemux != NULL) {
uint64_t time;
@@ -138,7 +159,7 @@
}
}
- return -1;
+ return INVALID_AV_SYNC_TIME;
}
sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
@@ -167,7 +188,10 @@
}
Result DemuxClient::connectCiCam(int ciCamId) {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->connectCiCam(ciCamId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
@@ -177,7 +201,10 @@
}
Result DemuxClient::disconnectCiCam() {
- // pending aidl interface
+ if (mTunerDemux != NULL) {
+ Status s = mTunerDemux->disconnectCiCam();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDemux != NULL) {
return mDemux->disconnectCiCam();
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 463944a..31eb35a 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -31,7 +31,9 @@
using Status = ::ndk::ScopedAStatus;
using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::android::hardware::tv::tuner::V1_0::IDemux;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
using ::android::hardware::tv::tuner::V1_0::DvrType;
using ::android::hardware::tv::tuner::V1_0::IDemux;
@@ -39,6 +41,9 @@
using namespace std;
+const int64_t INVALID_AV_SYNC_TIME = -1;
+const int INVALID_AV_SYNC_HW_ID = -1;
+
namespace android {
struct DemuxClient : public RefBase {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index ca9eb46..8b4ca37 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -18,6 +18,7 @@
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
+#include <fmq/ConvertMQDescriptors.h>
#include <utils/Log.h>
#include "FilterClient.h"
@@ -68,18 +69,12 @@
mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
}
-int FilterClient::read(uint8_t* buffer, int size) {
- // TODO: pending aidl interface
-
- if (mFilter != NULL) {
- Result res = getFilterMq();
- if (res != Result::SUCCESS) {
- return -1;
- }
- return copyData(buffer, size);
+int FilterClient::read(int8_t* buffer, int size) {
+ Result res = getFilterMq();
+ if (res != Result::SUCCESS) {
+ return -1;
}
-
- return -1;
+ return copyData(buffer, size);
}
SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
@@ -106,7 +101,10 @@
}
Result FilterClient::configureMonitorEvent(int monitorEventType) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureMonitorEvent(monitorEventType);
@@ -116,7 +114,10 @@
}
Result FilterClient::configureIpFilterContextId(int cid) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->configureIpFilterContextId(cid);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureIpCid(cid);
@@ -126,7 +127,19 @@
}
Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ int type;
+ switch (avStreamType.getDiscriminator()) {
+ case AvStreamType::hidl_discriminator::audio:
+ type = (int)avStreamType.audio();
+ break;
+ case AvStreamType::hidl_discriminator::video:
+ type = (int)avStreamType.video();
+ break;
+ }
+ Status s = mTunerFilter->configureAvStreamType(type);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter_1_1 != NULL) {
return mFilter_1_1->configureAvStreamType(avStreamType);
@@ -228,7 +241,10 @@
}
Result FilterClient::setDataSource(sp<FilterClient> filterClient){
- // TODO: pending aidl interface
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mFilter != NULL) {
sp<IFilter> sourceFilter = filterClient->getHalFilter();
@@ -891,29 +907,43 @@
}
Result FilterClient::getFilterMq() {
- if (mFilter == NULL) {
- return Result::INVALID_STATE;
- }
-
if (mFilterMQ != NULL) {
return Result::SUCCESS;
}
- Result getQueueDescResult = Result::UNKNOWN_ERROR;
- MQDescriptorSync<uint8_t> filterMQDesc;
- mFilter->getQueueDesc(
- [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
- filterMQDesc = desc;
- getQueueDescResult = r;
- });
- if (getQueueDescResult == Result::SUCCESS) {
- mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
- EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ AidlMQDesc aidlMqDesc;
+ Result res = Result::UNAVAILABLE;
+
+ if (mTunerFilter != NULL) {
+ Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
+ res = ClientHelper::getServiceSpecificErrorCode(s);
+ if (res == Result::SUCCESS) {
+ mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc);
+ EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ }
+ return res;
}
- return getQueueDescResult;
+
+ if (mFilter != NULL) {
+ MQDescriptorSync<uint8_t> filterMQDesc;
+ mFilter->getQueueDesc(
+ [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+ filterMQDesc = desc;
+ res = r;
+ });
+ if (res == Result::SUCCESS) {
+ AidlMQDesc aidlMQDesc;
+ unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+ filterMQDesc, &aidlMQDesc);
+ mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
+ EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+ }
+ }
+
+ return res;
}
-int FilterClient::copyData(uint8_t* buffer, int size) {
+int FilterClient::copyData(int8_t* buffer, int size) {
if (mFilter == NULL || mFilterMQ == NULL || mFilterMQEventFlag == NULL) {
return -1;
}
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 21919ac..bbabc28 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -25,12 +25,14 @@
#include <android/hardware/tv/tuner/1.1/IFilter.h>
#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
#include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/AidlMessageQueue.h>
#include <fmq/MessageQueue.h>
#include "ClientHelper.h"
#include "FilterClientCallback.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
@@ -69,10 +71,13 @@
using namespace std;
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
namespace android {
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
struct SharedHandleInfo {
native_handle_t* sharedHandle;
uint64_t size;
@@ -139,7 +144,7 @@
*
* @return the actual reading size. -1 if failed to read.
*/
- int read(uint8_t* buffer, int size);
+ int read(int8_t* buffer, int size);
/**
* Get the a/v shared memory handle information
@@ -234,7 +239,7 @@
void getAidlIpAddress(DemuxIpAddress ipAddr,
TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
Result getFilterMq();
- int copyData(uint8_t* buffer, int size);
+ int copyData(int8_t* buffer, int size);
void checkIsMediaFilter(DemuxFilterType type);
void handleAvShareMemory();
void closeAvSharedMemory();
@@ -259,7 +264,7 @@
*/
sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
- unique_ptr<MQ> mFilterMQ;
+ AidlMQ* mFilterMQ;
EventFlag* mFilterMQEventFlag;
sp<FilterClientCallback> mCallback;
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index 27ea6e5..432238d 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include <utils/Log.h>
+#include "ClientHelper.h"
#include "TimeFilterClient.h"
using ::android::hardware::tv::tuner::V1_0::Result;
@@ -28,13 +29,12 @@
/////////////// TimeFilterClient ///////////////////////
-// TODO: pending aidl interface
-TimeFilterClient::TimeFilterClient() {
- //mTunerTimeFilter = tunerTimeFilter;
+TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter) {
+ mTunerTimeFilter = tunerTimeFilter;
}
TimeFilterClient::~TimeFilterClient() {
- //mTunerTimeFilter = NULL;
+ mTunerTimeFilter = NULL;
mTimeFilter = NULL;
}
@@ -44,7 +44,10 @@
}
Result TimeFilterClient::setTimeStamp(long timeStamp) {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->setTimeStamp(timeStamp);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->setTimeStamp(timeStamp);
@@ -54,7 +57,10 @@
}
Result TimeFilterClient::clearTimeStamp() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->clearTimeStamp();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->clearTimeStamp();
@@ -64,7 +70,14 @@
}
long TimeFilterClient::getTimeStamp() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ int64_t timeStamp;
+ Status s = mTunerTimeFilter->getTimeStamp(&timeStamp);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return timeStamp;
+ }
if (mTimeFilter != NULL) {
Result res;
@@ -84,27 +97,37 @@
}
long TimeFilterClient::getSourceTime() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ int64_t sourceTime;
+ Status s = mTunerTimeFilter->getTimeStamp(&sourceTime);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ }
+ return sourceTime;
+ }
if (mTimeFilter != NULL) {
Result res;
- long timestamp;
+ long sourceTime;
mTimeFilter->getSourceTime(
[&](Result r, uint64_t t) {
res = r;
- timestamp = t;
+ sourceTime = t;
});
if (res != Result::SUCCESS) {
return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
}
- return timestamp;
+ return sourceTime;
}
return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
}
Result TimeFilterClient::close() {
- // TODO: pending aidl interface
+ if (mTunerTimeFilter != NULL) {
+ Status s = mTunerTimeFilter->close();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mTimeFilter != NULL) {
return mTimeFilter->close();
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
index 9a9d172..56ddd68 100644
--- a/media/jni/tuner/TimeFilterClient.h
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -17,12 +17,13 @@
#ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
#define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
-//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
+#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
#include <android/hardware/tv/tuner/1.1/types.h>
-//using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using Status = ::ndk::ScopedAStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hardware::hidl_vec;
@@ -36,8 +37,7 @@
struct TimeFilterClient : public RefBase {
public:
- // TODO: add TunerTimeFilter as parameter.
- TimeFilterClient();
+ TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter);
~TimeFilterClient();
// TODO: remove after migration to Tuner Service is done.
@@ -73,8 +73,7 @@
* An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client
* opens an TimeFilter. Default null when time filter is not opened.
*/
- // TODO: pending on aidl interface
- //shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
+ shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
/**
* A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 14393a1..a604490d 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -200,7 +200,14 @@
}
shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
- // pending aidl interface
+ if (mTunerService != NULL) {
+ TunerDemuxCapabilities aidlCaps;
+ Status s = mTunerService->getDemuxCaps(&aidlCaps);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps));
+ }
if (mTuner != NULL) {
Result res;
@@ -459,6 +466,26 @@
return descrambler;
}
+DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) {
+ DemuxCapabilities caps{
+ .numDemux = (uint32_t)aidlCaps.numDemux,
+ .numRecord = (uint32_t)aidlCaps.numRecord,
+ .numPlayback = (uint32_t)aidlCaps.numPlayback,
+ .numTsFilter = (uint32_t)aidlCaps.numTsFilter,
+ .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter,
+ .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter,
+ .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter,
+ .numPesFilter = (uint32_t)aidlCaps.numPesFilter,
+ .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter,
+ .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter,
+ .filterCaps = (uint32_t)aidlCaps.filterCaps,
+ .bTimeFilter = aidlCaps.bTimeFilter,
+ };
+ caps.linkCaps.resize(aidlCaps.linkCaps.size());
+ copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin());
+ return caps;
+}
+
FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
FrontendInfo hidlFrontendInfo {
.type = static_cast<FrontendType>(aidlFrontendInfo.type),
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ce6661..acd018e 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
using ::aidl::android::media::tv::tuner::ITunerService;
using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
@@ -145,6 +146,7 @@
sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
sp<IDescrambler> openHidlDescrambler();
vector<int> getLnbHandles();
+ DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
void updateTunerResources();
void updateFrontendResources();
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 2716e09..fa3213d 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3231,32 +3231,6 @@
}
}
- /** {@hide} - returns the factory serial number */
- @UnsupportedAppUsage
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public int registerNetworkFactory(Messenger messenger, String name) {
- try {
- return mService.registerNetworkFactory(messenger, name);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- @RequiresPermission(anyOf = {
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
- android.Manifest.permission.NETWORK_FACTORY})
- public void unregisterNetworkFactory(Messenger messenger) {
- try {
- mService.unregisterNetworkFactory(messenger);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
/**
* Registers the specified {@link NetworkProvider}.
* Each listener must only be registered once. The listener can be unregistered with
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e4..db8b7ed 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -156,9 +156,6 @@
boolean requestBandwidthUpdate(in Network network);
- int registerNetworkFactory(in Messenger messenger, in String name);
- void unregisterNetworkFactory(in Messenger messenger);
-
int registerNetworkProvider(in Messenger messenger, in String name);
void unregisterNetworkProvider(in Messenger messenger);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a6..a6e2af9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@
import android.provider.settings.validators.SecureSettingsValidators;
import android.provider.settings.validators.SystemSettingsValidators;
import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
@@ -95,10 +96,11 @@
private static final String KEY_NETWORK_POLICIES = "network_policies";
private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
- private static final int STATE_VERSION = 8;
+ private static final int STATE_VERSION = 9;
// Versioning of the Network Policies backup payload.
private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@
// Slots in the checksum array. Never insert new items in the middle
// of this array; new slots must be appended.
- private static final int STATE_SYSTEM = 0;
- private static final int STATE_SECURE = 1;
- private static final int STATE_LOCALE = 2;
- private static final int STATE_WIFI_SUPPLICANT = 3;
- private static final int STATE_WIFI_CONFIG = 4;
- private static final int STATE_GLOBAL = 5;
- private static final int STATE_LOCK_SETTINGS = 6;
- private static final int STATE_SOFTAP_CONFIG = 7;
- private static final int STATE_NETWORK_POLICIES = 8;
- private static final int STATE_WIFI_NEW_CONFIG = 9;
- private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SYSTEM = 0;
+ private static final int STATE_SECURE = 1;
+ private static final int STATE_LOCALE = 2;
+ private static final int STATE_WIFI_SUPPLICANT = 3;
+ private static final int STATE_WIFI_CONFIG = 4;
+ private static final int STATE_GLOBAL = 5;
+ private static final int STATE_LOCK_SETTINGS = 6;
+ private static final int STATE_SOFTAP_CONFIG = 7;
+ private static final int STATE_NETWORK_POLICIES = 8;
+ private static final int STATE_WIFI_NEW_CONFIG = 9;
+ private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
- private static final int STATE_SIZE = 11; // The current number of state items
+ private static final int STATE_SIZE = 12; // The current number of state items
// Number of entries in the checksum array at various version numbers
private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@
8, // version 5 added STATE_SOFTAP_CONFIG
9, // version 6 added STATE_NETWORK_POLICIES
10, // version 7 added STATE_WIFI_NEW_CONFIG
- STATE_SIZE // version 8 added STATE_DEVICE_CONFIG
+ 11, // version 8 added STATE_DEVICE_CONFIG
+ STATE_SIZE // version 9 added STATE_SIM_SPECIFIC_SETTINGS
};
private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
@@ -218,6 +222,7 @@
byte[] netPoliciesData = getNetworkPolicies();
byte[] wifiFullConfigData = getNewWifiConfigData();
byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+ byte[] simSpecificSettingsData = getSimSpecificSettingsData();
long[] stateChecksums = readOldChecksums(oldState);
@@ -246,6 +251,9 @@
stateChecksums[STATE_DEVICE_CONFIG] =
writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
deviceSpecificInformation, data);
+ stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+ writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+ KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -386,6 +394,12 @@
preservedSettings);
break;
+ case KEY_SIM_SPECIFIC_SETTINGS:
+ byte[] restoredSimSpecificSettings = new byte[size];
+ data.readEntityData(restoredSimSpecificSettings, 0, size);
+ restoreSimSpecificSettings(restoredSimSpecificSettings);
+ break;
+
default :
data.skipEntityData();
@@ -1189,6 +1203,20 @@
return true;
}
+ private byte[] getSimSpecificSettingsData() {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+ Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+ + " successfully retrieved");
+
+ return simSpecificData;
+ }
+
+ private void restoreSimSpecificSettings(byte[] data) {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ subManager.restoreAllSimSpecificSettingsFromBackup(data);
+ }
+
private void updateWindowManagerIfNeeded(Integer previousDensity) {
int newDensity;
try {
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6dff2e3..a6321fe 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -19,4 +19,7 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 44eeba1..bb04c3b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -565,4 +565,7 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">false</bool>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 3584c82..e2ca349 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -72,9 +72,13 @@
return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition());
}
+ /**
+ * Returns ActivityOptions for overriding task transition animation.
+ */
public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
int exitResId, final Runnable callback, final Handler callbackHandler) {
- return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+ return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+ callbackHandler,
new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1..e6477f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
import android.os.RemoteException;
import android.util.Log;
@@ -65,13 +67,17 @@
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(wallpapers);
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ RemoteAnimationTargetCompat.wrap(nonApps);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -83,8 +89,8 @@
}
}
};
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
- animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+ nonAppsCompat, animationFinishedCallback);
}
@Override
@@ -104,6 +110,9 @@
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+ // TODO(bc-unlock): Build wrapped object for non-apps target.
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ new RemoteAnimationTargetCompat[0];
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -147,7 +156,10 @@
}
}
t.apply();
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+ // TODO(bc-unlcok): Pass correct transit type.
+ remoteAnimationAdapter.onAnimationStart(
+ TRANSIT_OLD_NONE,
+ appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
};
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6..0076292 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
package com.android.systemui.shared.system;
+import android.view.WindowManager;
+
public interface RemoteAnimationRunnerCompat {
- void onAnimationStart(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+ void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
void onAnimationCancelled();
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca0..276036c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@
@Override
public void onDisplayChanged(int displayId) {
- if (displayId == DEFAULT_DISPLAY) return;
- final Presentation presentation = mPresentations.get(displayId);
- if (presentation != null && mShowing) {
- hidePresentation(displayId);
- // update DisplayInfo.
- final Display display = mDisplayService.getDisplay(displayId);
- if (display != null) {
- showPresentation(display);
- }
- }
+
}
@Override
@@ -305,15 +296,16 @@
}
@Override
+ public void onDisplayChanged() {
+ updateBounds();
+ getWindow().getDecorView().requestLayout();
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ updateBounds();
setContentView(LayoutInflater.from(mContext)
.inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@
mKeyguardClockSwitchController.init();
}
+
+ private void updateBounds() {
+ final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+ .getBounds();
+ mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+ mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e9..17f7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import android.app.ActivityTaskManager;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
@@ -26,8 +32,17 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ *
+ * Note: Must be consistent with WindowManagerService.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ private static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
@@ -52,6 +82,21 @@
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+ if (sEnableRemoteKeyguardAnimation) {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter exitAnimationAdapter =
+ new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ exitAnimationAdapter);
+ final RemoteAnimationAdapter occludeAnimationAdapter =
+ new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+ ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+ DEFAULT_DISPLAY, definition);
+ }
}
@Override
@@ -76,6 +121,48 @@
}
}
+ private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+ checkPermission();
+ mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+ null /* nonApps */, finishedCallback);
+ Trace.endSection();
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
+ private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+ // run animation.
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
@Override // Binder interface
@@ -225,6 +312,11 @@
mKeyguardViewMediator.onBootCompleted();
}
+ /**
+ * @deprecated When remote animation is enabled, this won't be called anymore. Use
+ * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+ */
+ @Deprecated
@Override
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e732669..5a918d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
@@ -65,8 +68,13 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -85,6 +93,7 @@
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -1318,6 +1327,7 @@
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
// SHOW_WHEN_LOCKED activity instead.
+ // TODO(bc-unlock): Migrate to remote animation.
startKeyguardExitAnimation(0, 0);
}
@@ -1703,7 +1713,9 @@
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
- handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+ handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+ params.mApps, params.mWallpapers, params.mNonApps,
+ params.mFinishedCallback);
mFalsingCollector.onSuccessfulUnlock();
Trace.endSection();
break;
@@ -1990,15 +2002,19 @@
if (mShowing && !mOccluded) {
mKeyguardGoingAwayRunnable.run();
} else {
+ // TODO(bc-unlock): Fill parameters
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
- mHideAnimation.getDuration());
+ mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */,
+ null /* nonApps */, null /* finishedCallback */);
}
}
Trace.endSection();
}
- private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2047,49 @@
mWakeAndUnlocking = false;
mDismissCallbackRegistry.notifyDismissSucceeded();
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+ // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+ // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+ // supported, so it's always null.
+ mContext.getMainExecutor().execute(() -> {
+ if (finishedCallback == null) {
+ return;
+ }
+
+ // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+ final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+ final RemoteAnimationTarget primary = apps[0];
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.setDuration(400 /* duration */);
+ anim.setInterpolator(Interpolators.LINEAR);
+ anim.addUpdateListener((ValueAnimator animation) -> {
+ SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction())
+ .build();
+ applier.scheduleApply(params);
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+ });
+ anim.start();
+ });
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
@@ -2223,10 +2282,55 @@
return mKeyguardViewControllerLazy.get();
}
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+ * animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @deprecated Will be migrate to remote animation soon.
+ */
+ @Deprecated
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and start running keyguard exit animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
- new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+ new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+ wallpapers, nonApps, finishedCallback));
mHandler.sendMessage(msg);
Trace.endSection();
}
@@ -2300,12 +2404,26 @@
private static class StartKeyguardExitAnimParams {
+ @WindowManager.TransitionOldType int mTransit;
long startTime;
long fadeoutDuration;
+ RemoteAnimationTarget[] mApps;
+ RemoteAnimationTarget[] mWallpapers;
+ RemoteAnimationTarget[] mNonApps;
+ IRemoteAnimationFinishedCallback mFinishedCallback;
- private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+ private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ this.mTransit = transit;
this.startTime = startTime;
this.fadeoutDuration = fadeoutDuration;
+ this.mApps = apps;
+ this.mWallpapers = wallpapers;
+ this.mNonApps = nonApps;
+ this.mFinishedCallback = finishedCallback;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e9189..580cbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -36,12 +34,9 @@
import android.util.Log;
import android.view.ViewGroup;
-import androidx.preference.PreferenceManager;
-
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.util.List;
@@ -128,26 +123,17 @@
/** Stores the user selected configuration for {@code mAppWidgetId}. */
private void storeWidgetConfiguration(PeopleSpaceTile tile) {
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
if (PeopleSpaceUtils.DEBUG) {
Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
+ tile.getId() + " for widget ID: "
+ mAppWidgetId);
}
- // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
- editor.putString(String.valueOf(mAppWidgetId), tile.getId());
- editor.putInt(tile.getId(), mAppWidgetId);
- editor.apply();
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
- Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
- appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
- int[] widgetIds = appWidgetManager.getAppWidgetIds(
- new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+ PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+ int[] widgetIds = new int[mAppWidgetId];
// TODO: Populate new widget with existing conversation notification, if there is any.
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
- mNotificationManager);
+ mPeopleManager);
finishActivity();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968..994dc6d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@
import static android.app.Notification.EXTRA_MESSAGES;
+import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -70,11 +71,13 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -89,6 +92,13 @@
private static final int MIN_HOUR = 1;
private static final int ONE_DAY = 1;
public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+ public static final String PACKAGE_NAME = "package_name";
+ public static final String USER_ID = "user_id";
+ public static final String SHORTCUT_ID = "shortcut_id";
+
+ public static final String EMPTY_STRING = "";
+ public static final int INVALID_WIDGET_ID = -1;
+ public static final int INVALID_USER_ID = -1;
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -171,70 +181,174 @@
* notification being posted or removed.
*/
public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
- IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE));
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
- try {
- List<PeopleSpaceTile> tiles =
- PeopleSpaceUtils.getTiles(context, notificationManager,
- peopleManager, launcherApps);
- for (int appWidgetId : appWidgetIds) {
- String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
- if (DEBUG) {
- Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
- }
-
- Optional<PeopleSpaceTile> entry = tiles.stream().filter(
- e -> e.getId().equals(shortcutId)).findFirst();
-
- if (!entry.isPresent() || shortcutId == null) {
- if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
- //TODO: Delete app widget id when crash is fixed (b/175486868)
- continue;
- }
- // Augment current tile based on stored fields.
- PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
- appWidgetId);
-
- RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
- // Tell the AppWidgetManager to perform an update on the current app widget.
- appWidgetManager.updateAppWidget(appWidgetId, views);
-
- widgetIdToTile.put(appWidgetId, tile);
+ for (int appWidgetId : appWidgetIds) {
+ PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
+ if (tile == null) {
+ if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+ //TODO: Delete app widget id when crash is fixed (b/172932636)
+ continue;
}
- } catch (Exception e) {
- Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+
+ if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+ RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+ // Tell the AppWidgetManager to perform an update on the current app widget.
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+
+ widgetIdToTile.put(appWidgetId, tile);
}
getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
}
- /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
- @VisibleForTesting
- static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
- AppWidgetManager appWidgetManager, int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
- if (storedTile == null) {
- return tile;
+ @Nullable
+ private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+ AppWidgetManager appWidgetManager,
+ Context context, int appWidgetId) {
+ try {
+ // Migrate storage for existing users.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+ if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+ if (shortcutId == null) {
+ Log.e(TAG, "Cannot restore widget");
+ return null;
+ }
+ migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+ pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ }
+
+ // Check if tile is cached.
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ if (tile != null) {
+ return tile;
+ }
+
+ // If tile is null, we need to retrieve from persisted storage.
+ if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+ if (channel == null) {
+ Log.d(TAG, "Could not retrieve conversation from storage");
+ return null;
+ }
+ return new PeopleSpaceTile.Builder(channel, launcherApps).build();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+ return null;
}
- return tile.toBuilder()
- .setBirthdayText(storedTile.getBirthdayText())
- .setNotificationKey(storedTile.getNotificationKey())
- .setNotificationContent(storedTile.getNotificationContent())
- .setNotificationDataUri(storedTile.getNotificationDataUri())
- .build();
}
- /** If incoming notification changed tile, store the changes in the tile options. */
- public static void storeNotificationChange(StatusBarNotification sbn,
+ /** Best-effort attempts to migrate existing users to the new storage format. */
+ // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+ private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+ int appWidgetId) {
+ try {
+ List<PeopleSpaceTile> tiles =
+ PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+ IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)),
+ context.getSystemService(LauncherApps.class));
+ Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+ e -> e.getId().equals(shortcutId)).findFirst();
+ if (entry.isPresent()) {
+ if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+ setStorageForTile(context, entry.get(), appWidgetId);
+ } else {
+ Log.e(TAG, "Could not migrate user");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not query conversations");
+ }
+ }
+
+ /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+ public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+ // Write relevant persisted storage.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+ widgetEditor.apply();
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(appWidgetId), tile.getId());
+ String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+ // Don't overwrite existing widgets with the same key.
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(appWidgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+
+ // Write cached storage.
+ updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+ tile);
+ }
+
+ /** Removes stored data when tile is deleted. */
+ public static void removeStorageForTile(Context context, int widgetId) {
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ String packageName = widgetSp.getString(PACKAGE_NAME, null);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ int userId = widgetSp.getInt(USER_ID, -1);
+
+ // Delete widgetId mapping to key.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.remove(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.remove(String.valueOf(widgetId));
+ editor.apply();
+
+ // Delete all data specifically mapped to widgetId.
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.remove(PACKAGE_NAME);
+ widgetEditor.remove(USER_ID);
+ widgetEditor.remove(SHORTCUT_ID);
+ widgetEditor.apply();
+ }
+
+ /**
+ * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+ * requested update data.
+ */
+ public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+ String packageName, int userId) {
+ SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+ String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+ return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+ && storedUserId == userId;
+ }
+
+ /**
+ * If incoming notification changed tile, store the changes in the tile options.
+ */
+ public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+ Context context,
+ StatusBarNotification sbn,
NotificationAction notificationAction, AppWidgetManager appWidgetManager,
int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
@@ -263,7 +377,7 @@
.setNotificationDataUri(null)
.build();
}
- updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+ updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
}
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
@@ -677,4 +791,23 @@
}
return lookupKeysWithBirthdaysToday;
}
+
+ /**
+ * Returns the uniquely identifying key for the conversation.
+ *
+ * <p>{@code userId} will always be a number, so we put user ID as the
+ * delimiter between the app-provided strings of shortcut ID and package name.
+ *
+ * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+ * a {@code packageName} to always start with a letter. This restriction means we are
+ * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+ * case is impossible given the package name restrictions:
+ * <ul>
+ * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+ * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+ * </ul>
+ */
+ public static String getKey(String shortcutId, String packageName, int userId) {
+ return shortcutId + "/" + userId + "/" + packageName;
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index ad6046b..bee54e4 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +29,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -51,7 +53,7 @@
private final Context mContext;
private IAppWidgetService mAppWidgetService;
private AppWidgetManager mAppWidgetManager;
- private INotificationManager mNotificationManager;
+ private IPeopleManager mPeopleManager;
@Inject
public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@
mContext = context;
mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mPeopleManager = IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
}
- /** Constructor used for testing. */
+ /**
+ * Constructor used for testing.
+ */
@VisibleForTesting
protected PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@
ServiceManager.getService(Context.APPWIDGET_SERVICE));
}
- /** AppWidgetManager setter used for testing. */
+ /**
+ * AppWidgetManager setter used for testing.
+ */
@VisibleForTesting
protected void setAppWidgetManager(IAppWidgetService appWidgetService,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
mAppWidgetService = appWidgetService;
mAppWidgetManager = appWidgetManager;
- mNotificationManager = notificationManager;
+ mPeopleManager = peopleManager;
}
- /** Updates People Space widgets. */
+ /**
+ * Updates People Space widgets.
+ */
public void updateWidgets() {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
- mAppWidgetManager, mNotificationManager);
+ mAppWidgetManager, mPeopleManager);
} else {
mAppWidgetService
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,38 @@
* Check if any existing People tiles match the incoming notification change, and store the
* change in the tile if so.
*/
- public void storeNotificationChange(StatusBarNotification sbn,
+ public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
- if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+ RemoteViews views = new RemoteViews(
+ mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+ if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (!showSingleConversation) {
return;
}
try {
+ String sbnShortcutId = sbn.getShortcutId();
+ if (sbnShortcutId == null) {
+ return;
+ }
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
if (widgetIds.length == 0) {
return;
}
-
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- for (int widgetId : widgetIds) {
- String shortcutId = sp.getString(String.valueOf(widgetId), null);
- if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
- continue;
- }
+ int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+ String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ if (storedWidgetIds.isEmpty()) {
+ return;
+ }
+ for (String widgetIdString : storedWidgetIds) {
+ int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
- PeopleSpaceUtils.storeNotificationChange(
+ PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
sbn, notificationAction, mAppWidgetManager, widgetId);
}
} catch (Exception e) {
@@ -159,8 +175,7 @@
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
}
@Override
@@ -169,8 +184,7 @@
NotificationListenerService.RankingMap rankingMap
) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -179,8 +193,7 @@
NotificationListenerService.RankingMap rankingMap,
int reason) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -207,4 +220,4 @@
}
};
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc..f5577d3 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
@@ -53,8 +53,8 @@
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
- appWidgetManager, INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+ appWidgetManager, IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)));
return;
}
// Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+ PeopleSpaceUtils.removeStorageForTile(context, widgetId);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6c..d08f973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
+import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@
}
@Override
- public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] remoteAnimationTargets,
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+ RemoteAnimationTarget[] remoteAnimationNonAppTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a30f193..3b47594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -95,6 +95,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -292,6 +293,7 @@
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final QSDetailDisplayer mQSDetailDisplayer;
+ private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -554,7 +556,9 @@
QSDetailDisplayer qsDetailDisplayer,
ScrimController scrimController,
MediaDataManager mediaDataManager,
- AmbientState ambientState) {
+ AmbientState ambientState,
+ FeatureFlags featureFlags
+ ) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -571,6 +575,7 @@
mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
+ mFeatureFlags = featureFlags;
mView.setWillNotDraw(!DEBUG);
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
@@ -768,6 +773,26 @@
lp.width = panelWidth;
mNotificationStackScrollLayoutController.setLayoutParams(lp);
}
+
+ if (shouldUseSplitNotificationShade()) {
+ // In order to change the constraints at runtime, all children of the Constraint Layout
+ // must have ids.
+ ensureAllViewsHaveIds(mNotificationContainerParent);
+ }
+ }
+
+ private boolean shouldUseSplitNotificationShade() {
+ return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
+ && mResources.getBoolean(R.bool.config_use_split_notification_shade);
+ }
+
+ private static void ensureAllViewsHaveIds(ViewGroup parentView) {
+ for (int i = 0; i < parentView.getChildCount(); i++) {
+ View childView = parentView.getChildAt(i);
+ if (childView.getId() == View.NO_ID) {
+ childView.setId(View.generateViewId());
+ }
+ }
}
private void reInflateViews() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index b7d1bc6..6db21f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -362,34 +362,6 @@
}
@Test
- public void testAugmentTileFromStorageWithNotification() {
- PeopleSpaceTile tile =
- new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
- .build();
- PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
-
- assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
- assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
- assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
- }
-
- @Test
- public void testAugmentTileFromStorageWithoutNotification() {
- PeopleSpaceTile tile =
- new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
- .build();
- PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
-
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationDataUri()).isEqualTo(null);
- }
-
- @Test
public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
when(mMockCursor.moveToNext()).thenReturn(true, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 8c0afb8..ef314ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -21,6 +21,8 @@
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -31,26 +33,24 @@
import static java.util.Objects.requireNonNull;
-import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final long MIN_LINGER_DURATION = 5;
- private static final String TEST_PACKAGE_A = "com.test.package_a";
+ private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
private static final String TEST_PACKAGE_B = "com.test.package_b";
private static final String TEST_CHANNEL_ID = "channel_id";
private static final String TEST_CHANNEL_NAME = "channel_name";
private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
private static final String TEST_CONVERSATION_ID = "conversation_id";
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+ private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
private static final String SHORTCUT_ID = "101";
private static final String OTHER_SHORTCUT_ID = "102";
- private static final String NOTIFICATION_KEY = "notification_key";
- private static final String NOTIFICATION_CONTENT = "notification_content";
+ private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+ private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
- .setNotificationKey(NOTIFICATION_KEY)
+ .setPackageName(TEST_PACKAGE_A)
+ .setUid(0)
+ .setNotificationKey(NOTIFICATION_KEY + "1")
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
+ private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+ SHORTCUT_ID).setLongLabel("name").build();
private PeopleSpaceWidgetManager mManager;
@@ -119,175 +125,101 @@
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
- private INotificationManager mINotificationManager;
+ private IPeopleManager mIPeopleManager;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+ @Captor
+ private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mManager =
new PeopleSpaceWidgetManager(mContext);
- mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+ mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
mManager.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
mNoMan.addListener(serviceListener);
+ // Default to single People tile widgets.
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+ Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
- editor.apply();
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
+ when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
+ getConversationWithShortcutId(SHORTCUT_ID));
}
@Test
- public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbn)
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+ public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ )
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(new SbnBuilder()
+ .setNotification(notificationWithoutShortcut)
+ .setPkg(TEST_PACKAGE_A)
+ .build())
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
- int[] widgetIdsArray = {1};
+ public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+ int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithoutPackageName)
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
-
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -298,13 +230,12 @@
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+ public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -316,45 +247,57 @@
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+ public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(4);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,215 @@
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(4);
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+ throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- Notification notification = new Notification.Builder(mContext)
+ Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(SHORTCUT_ID)
.build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(notificationWithoutMessagingStyle)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
- public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, times(2))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(null);
+ assertThat(tile.getNotificationContent()).isEqualTo(null);
+ assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+ any());
}
- /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
- private List<ConversationChannelWrapper> getConversationWithShortcutId() {
- List<ConversationChannelWrapper> convos = new ArrayList<>();
- ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
- convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
- "name").build());
- convos.add(convo1);
- return convos;
+ /**
+ * Returns a single conversation associated with {@code shortcutId}.
+ */
+ private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build();
+ ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+ 0L, false);
+ return convo;
}
- private StatusBarNotification createConversationNotification(String shortcutId) {
- Notification notification = new Notification.Builder(mContext)
+ private Notification createMessagingStyleNotification(String shortcutId) {
+ return new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(shortcutId)
.setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ PERSON))
)
.build();
+ }
+
+ private StatusBarNotification createConversationNotification(String shortcutId) {
+ Notification notification = createMessagingStyleNotification(shortcutId);
return new SbnBuilder()
.setNotification(notification)
+ .setPkg(TEST_PACKAGE_A)
.build();
}
+
+ private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+ widgetEditor.apply();
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(widgetId), shortcutId);
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 8d4470b..fa78ca6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -68,6 +68,7 @@
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -204,6 +205,10 @@
@Mock
private MediaDataManager mMediaDataManager;
@Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
+ private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ @Mock
private AmbientState mAmbientState;
private NotificationPanelViewController mNotificationPanelViewController;
private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -219,6 +224,8 @@
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
mDisplayMetrics.density = 100;
when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+ when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
+ when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
@@ -237,6 +244,8 @@
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ when(mView.findViewById(R.id.notification_container_parent))
+ .thenReturn(mNotificationContainerParent);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
@@ -264,6 +273,11 @@
.thenReturn(mKeyguardClockSwitchController);
when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
.thenReturn(mKeyguardStatusViewController);
+ when(mQsFrame.getLayoutParams()).thenReturn(
+ new ViewGroup.LayoutParams(600, 400));
+ when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn(
+ new ViewGroup.LayoutParams(600, 400));
+
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
mLayoutInflater,
@@ -285,7 +299,8 @@
new QSDetailDisplayer(),
mScrimController,
mMediaDataManager,
- mAmbientState);
+ mAmbientState,
+ mFeatureFlags);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
@@ -400,6 +415,25 @@
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
+ @Test
+ public void testAllChildrenOfNotificationContainer_haveIds() {
+ when(mNotificationContainerParent.getChildCount()).thenReturn(2);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+ when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+
+ View view1 = new View(mContext);
+ view1.setId(1);
+ when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1);
+
+ View view2 = mock(View.class);
+ when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2);
+
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1);
+ assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID);
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd8..9ccb0c7 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1137,7 +1137,8 @@
*/
public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler);
/**
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 0000000..20ebe29
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+ private BundleUtils() {
+ }
+
+ /**
+ * Returns true if {@code in} is null or empty.
+ */
+ public static boolean isEmpty(@Nullable Bundle in) {
+ return (in == null) || (in.size() == 0);
+ }
+
+ /**
+ * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
+ * bundle.
+ *
+ * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+ * {@link Bundle#EMPTY})
+ */
+ public static @NonNull Bundle clone(@Nullable Bundle in) {
+ return (in != null) ? new Bundle(in) : new Bundle();
+ }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 08390b4..bf9d564 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1227,6 +1227,14 @@
mDnsManager = new DnsManager(mContext, mDnsResolver);
registerPrivateDnsSettingsCallbacks();
+
+ mNoServiceNetwork = new NetworkAgentInfo(null,
+ new Network(NO_SERVICE_NET_ID),
+ new NetworkInfo(TYPE_NONE, 0, "", ""),
+ new LinkProperties(), new NetworkCapabilities(), 0, mContext,
+ null, new NetworkAgentConfig(), this, null,
+ null, null, 0, INVALID_UID,
+ mQosCallbackTracker);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1376,7 +1384,7 @@
}
private NetworkState getUnfilteredActiveNetworkState(int uid) {
- NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
final Network[] networks = getVpnUnderlyingNetworks(uid);
if (networks != null) {
@@ -1509,7 +1517,7 @@
}
}
- NetworkAgentInfo nai = getDefaultNetwork();
+ NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
ignoreBlocked)) {
return null;
@@ -1648,21 +1656,28 @@
HashMap<Network, NetworkCapabilities> result = new HashMap<>();
- final NetworkAgentInfo nai = getDefaultNetwork();
- NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
- if (nc != null) {
- result.put(
- nai.network,
- createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (!nri.isBeingSatisfied()) {
+ continue;
+ }
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+ if (null != nc
+ && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ && !result.containsKey(nai.network)) {
+ result.put(
+ nai.network,
+ createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+ nc, mDeps.getCallingUid(), callingPackageName));
+ }
}
// No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
- if (networks != null) {
- for (Network network : networks) {
- nc = getNetworkCapabilitiesInternal(network);
- if (nc != null) {
+ if (null != networks) {
+ for (final Network network : networks) {
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+ if (null != nc) {
result.put(
network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@@ -1684,9 +1699,7 @@
/**
* Return LinkProperties for the active (i.e., connected) default
- * network interface. It is assumed that at most one default network
- * is active at a time. If more than one is active, it is indeterminate
- * which will be returned.
+ * network interface for the calling uid.
* @return the ip properties for the active network, or {@code null} if
* none is active
*/
@@ -2129,8 +2142,8 @@
private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
boolean isBackgroundRestricted) {
- return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
- isNetworkMetered, isBackgroundRestricted);
+ return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted);
}
/**
@@ -2713,9 +2726,9 @@
pw.println(nai.requestAt(i).toString());
}
pw.decreaseIndent();
- pw.println("Lingered:");
+ pw.println("Inactivity Timers:");
pw.increaseIndent();
- nai.dumpLingerTimers(pw);
+ nai.dumpInactivityTimers(pw);
pw.decreaseIndent();
pw.decreaseIndent();
}
@@ -3310,27 +3323,27 @@
}
/**
- * Updates the linger state from the network requests inside the NAI.
+ * Updates the inactivity state from the network requests inside the NAI.
* @param nai the agent info to update
* @param now the timestamp of the event causing this update
- * @return whether the network was lingered as a result of this update
+ * @return whether the network was inactive as a result of this update
*/
- private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
- // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
- // 2. If the network was lingering and there are now requests, unlinger it.
+ private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+ // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+ // 2. If the network was inactive and there are now requests, unset inactive.
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
- // one lingered request, start lingering.
- nai.updateLingerTimer();
+ // one lingered request, set inactive.
+ nai.updateInactivityTimer();
if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.toShortString());
- nai.unlinger();
+ if (DBG) log("Unsetting inactive " + nai.toShortString());
+ nai.unsetInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
- } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+ } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
if (DBG) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
- log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+ log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
}
- nai.linger();
+ nai.setInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
return true;
}
@@ -3344,7 +3357,6 @@
if (VDBG) log("NetworkFactory connected");
// Finish setting up the full connection
NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
- npi.completeConnection();
sendAllRequestsToProvider(npi);
} else {
loge("Error connecting NetworkFactory");
@@ -3446,13 +3458,16 @@
propagateUnderlyingNetworkCapabilities(nai.network);
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
- NetworkRequest request = nai.requestAt(i);
+ final NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
+ // uid rules for this network will be removed in destroyNativeNetwork(nai).
nri.setSatisfier(null, null);
- sendUpdatedScoreToFactories(request, null);
+ if (request.isRequest()) {
+ sendUpdatedScoreToFactories(request, null);
+ }
if (mDefaultRequest == nri) {
// TODO : make battery stats aware that since 2013 multiple interfaces may be
@@ -3466,7 +3481,7 @@
}
}
}
- nai.clearLingerState();
+ nai.clearInactivityState();
// TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
// Currently, deleting it breaks tests that check for the default network disconnecting.
// Find out why, fix the rematch code, and delete this.
@@ -3565,8 +3580,8 @@
}
}
rematchAllNetworksAndRequests();
- // If an active request exists, return as its score has already been sent if needed.
- if (null != nri.getActiveRequest()) {
+ // If the nri is satisfied, return as its score has already been sent if needed.
+ if (nri.isBeingSatisfied()) {
return;
}
@@ -3709,7 +3724,7 @@
if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
return;
}
- if (nri.getSatisfier() != null) {
+ if (nri.isBeingSatisfied()) {
return;
}
if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
@@ -3808,7 +3823,7 @@
// If there are still lingered requests on this network, don't tear it down,
// but resume lingering instead.
final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
if (unneeded(nai, UnneededFor.TEARDOWN)) {
@@ -4900,7 +4915,8 @@
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- final NetworkAgentInfo defaultNai = getDefaultNetwork();
+ final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
+ nai.networkCapabilities.getOwnerUid());
if (defaultNai != null) {
underlyingNetworks = new Network[] { defaultNai.network };
}
@@ -4951,8 +4967,10 @@
}
}
- private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
- final Network defaultNetwork = getNetwork(getDefaultNetwork());
+ // TODO This needs to be the default network that applies to the NAI.
+ private Network[] underlyingNetworksOrDefault(final int ownerUid,
+ Network[] underlyingNetworks) {
+ final Network defaultNetwork = getNetwork(getDefaultNetworkForUid(ownerUid));
if (underlyingNetworks == null && defaultNetwork != null) {
// null underlying networks means to track the default.
underlyingNetworks = new Network[] { defaultNetwork };
@@ -4965,7 +4983,8 @@
// TODO: support more than one level of underlying networks, either via a fixed-depth search
// (e.g., 2 levels of underlying networks), or via loop detection, or....
if (!nai.supportsUnderlyingNetworks()) return false;
- final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+ final Network[] underlying = underlyingNetworksOrDefault(
+ nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
return ArrayUtils.contains(underlying, network);
}
@@ -5423,27 +5442,21 @@
private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
- private final AsyncChannel mAsyncChannel;
private final IBinder.DeathRecipient mDeathRecipient;
public final int providerId;
NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
- int providerId, IBinder.DeathRecipient deathRecipient) {
+ int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
this.name = name;
this.messenger = messenger;
this.providerId = providerId;
- mAsyncChannel = asyncChannel;
mDeathRecipient = deathRecipient;
- if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
- throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+ if (mDeathRecipient == null) {
+ throw new AssertionError("Must pass a deathRecipient");
}
}
- boolean isLegacyNetworkFactory() {
- return mAsyncChannel != null;
- }
-
void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
try {
messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5454,38 +5467,19 @@
}
void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
- servingProviderId, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+ sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
servingProviderId, request);
- }
}
void cancelRequest(NetworkRequest request) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
- } else {
- sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
- }
+ sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
}
void connect(Context context, Handler handler) {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.connect(context, handler, messenger);
- } else {
- try {
- messenger.getBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- mDeathRecipient.binderDied();
- }
- }
- }
-
- void completeConnection() {
- if (isLegacyNetworkFactory()) {
- mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ try {
+ messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ mDeathRecipient.binderDied();
}
}
}
@@ -5521,9 +5515,8 @@
mActiveRequest = activeRequest;
}
- // The network currently satisfying this request, or null if none. Must only be touched
- // on the handler thread. This only makes sense for network requests and not for listens,
- // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+ // The network currently satisfying this NRI. Only one request in an NRI can have a
+ // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier.
@Nullable
private NetworkAgentInfo mSatisfier;
NetworkAgentInfo getSatisfier() {
@@ -5546,6 +5539,18 @@
final int mUid;
final Messenger messenger;
+ /**
+ * Get the list of UIDs this nri applies to.
+ */
+ @NonNull
+ private Set<UidRange> getUids() {
+ // networkCapabilities.getUids() returns a defensive copy.
+ // multilayer requests will all have the same uids so return the first one.
+ final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
+ ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
+ return uids;
+ }
+
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
@@ -5579,6 +5584,13 @@
this(r, null);
}
+ // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
+ // set to the mNoServiceNetwork in which case mActiveRequest will be null thus returning
+ // false.
+ boolean isBeingSatisfied() {
+ return (null != mSatisfier && null != mActiveRequest);
+ }
+
boolean isMultilayerRequest() {
return mRequests.size() > 1;
}
@@ -5604,7 +5616,9 @@
@Override
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+ return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+ + (mActiveRequest == null ? null : mActiveRequest.requestId)
+ + " " + mRequests
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -5960,15 +5974,6 @@
EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
}
- @Override
- public int registerNetworkFactory(Messenger messenger, String name) {
- enforceNetworkFactoryPermission();
- NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
- nextNetworkProviderId(), null /* deathRecipient */);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
- return npi.providerId;
- }
-
private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -5982,10 +5987,7 @@
if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
mNetworkProviderInfos.put(npi.messenger, npi);
npi.connect(mContext, mTrackerHandler);
- if (!npi.isLegacyNetworkFactory()) {
- // Legacy NetworkFactories get their requests when their AsyncChannel connects.
- sendAllRequestsToProvider(npi);
- }
+ sendAllRequestsToProvider(npi);
}
@Override
@@ -6004,11 +6006,6 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
}
- @Override
- public void unregisterNetworkFactory(Messenger messenger) {
- unregisterNetworkProvider(messenger);
- }
-
private void handleUnregisterNetworkProvider(Messenger messenger) {
NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
if (npi == null) {
@@ -6064,6 +6061,26 @@
@NonNull
private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
+ private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
+ return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
+ }
+
+ /**
+ * Determine if an nri is a managed default request that disallows default networking.
+ * @param nri the request to evaluate
+ * @return true if device-default networking is disallowed
+ */
+ private boolean isDefaultBlocked(@NonNull final NetworkRequestInfo nri) {
+ // Check if this nri is a managed default that supports the default network at its
+ // lowest priority request.
+ final NetworkRequest defaultNetworkRequest = mDefaultRequest.mRequests.get(0);
+ final NetworkCapabilities lowestPriorityNetCap =
+ nri.mRequests.get(nri.mRequests.size() - 1).networkCapabilities;
+ return isPerAppDefaultRequest(nri)
+ && !(defaultNetworkRequest.networkCapabilities.equalRequestableCapabilities(
+ lowestPriorityNetCap));
+ }
+
// Request used to optionally keep mobile data active even when higher
// priority networks like Wi-Fi are active.
private final NetworkRequest mDefaultMobileDataRequest;
@@ -6075,12 +6092,39 @@
// Request used to optionally keep vehicle internal network always active
private final NetworkRequest mDefaultVehicleRequest;
- // TODO: b/178729499 update this in favor of a method taking in a UID.
+ // TODO replace with INetd.DUMMY_NET_ID when available.
+ private static final int NO_SERVICE_NET_ID = 51;
+ // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
+ // network with no service. This NAI should never be matched against, nor should any public API
+ // ever return the associated network. For this reason, this NAI is not in the list of available
+ // NAIs. It is used in computeNetworkReassignment() to be set as the satisfier for non-device
+ // default requests that don't support using the device default network which will ultimately
+ // allow ConnectivityService to use this no-service network when calling makeDefaultForApps().
+ @VisibleForTesting
+ final NetworkAgentInfo mNoServiceNetwork;
+
// The NetworkAgentInfo currently satisfying the default request, if any.
private NetworkAgentInfo getDefaultNetwork() {
return mDefaultRequest.mSatisfier;
}
+ private NetworkAgentInfo getDefaultNetworkForUid(final int uid) {
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ // Currently, all network requests will have the same uids therefore checking the first
+ // one is sufficient. If/when uids are tracked at the nri level, this can change.
+ final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+ if (null == uids) {
+ continue;
+ }
+ for (final UidRange range : uids) {
+ if (range.contains(uid)) {
+ return nri.getSatisfier();
+ }
+ }
+ }
+ return getDefaultNetwork();
+ }
+
@Nullable
private Network getNetwork(@Nullable NetworkAgentInfo nai) {
return nai != null ? nai.network : null;
@@ -6165,8 +6209,6 @@
LinkProperties lp = new LinkProperties(linkProperties);
- // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
- // satisfies mDefaultRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6589,7 +6631,8 @@
@VisibleForTesting
void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
@NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
- underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+ underlyingNetworks = underlyingNetworksOrDefault(
+ agentCaps.getOwnerUid(), underlyingNetworks);
int[] transportTypes = agentCaps.getTransportTypes();
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -7195,7 +7238,7 @@
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
- oldNetwork.clearLingerState();
+ oldNetwork.clearInactivityState();
if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
// Tear the network down.
@@ -7233,21 +7276,20 @@
log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
}
- try {
- // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
- if (mDefaultRequest != nri) {
- return;
- }
-
- if (null != newDefaultNetwork) {
- mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
- } else {
- mNetd.networkClearDefault();
- }
- } catch (RemoteException | ServiceSpecificException e) {
- loge("Exception setting default network :" + e);
+ // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+ if (newDefaultNetwork != null) {
+ propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
}
+ // Set an app level managed default and return since further processing only applies to the
+ // default network.
+ if (mDefaultRequest != nri) {
+ makeDefaultForApps(nri, oldDefaultNetwork, newDefaultNetwork);
+ return;
+ }
+
+ makeDefaultNetwork(newDefaultNetwork);
+
if (oldDefaultNetwork != null) {
mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
}
@@ -7258,10 +7300,6 @@
updateTcpBufferSizes(null != newDefaultNetwork
? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
- // Fix up the NetworkCapabilities of any networks that have this network as underlying.
- if (newDefaultNetwork != null) {
- propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
- }
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
@@ -7285,6 +7323,49 @@
prevNetwork, prevScore, prevLp, prevNc);
}
+ private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork) {
+ try {
+ if (VDBG) {
+ log("Setting default network for " + nri
+ + " using UIDs " + nri.getUids()
+ + " with old network " + (oldDefaultNetwork != null
+ ? oldDefaultNetwork.network().getNetId() : "null")
+ + " and new network " + (newDefaultNetwork != null
+ ? newDefaultNetwork.network().getNetId() : "null"));
+ }
+ if (nri.getUids().isEmpty()) {
+ throw new IllegalStateException("makeDefaultForApps called without specifying"
+ + " any applications to set as the default." + nri);
+ }
+ if (null != newDefaultNetwork) {
+ mNetd.networkAddUidRanges(
+ newDefaultNetwork.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ }
+ if (null != oldDefaultNetwork) {
+ mNetd.networkRemoveUidRanges(
+ oldDefaultNetwork.network.getNetId(),
+ toUidRangeStableParcels(nri.getUids()));
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception setting OEM network preference default network :" + e);
+ }
+ }
+
+ private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+ try {
+ if (null != newDefaultNetwork) {
+ mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
+ } else {
+ mNetd.networkClearDefault();
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception setting default network :" + e);
+ }
+ }
+
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
// For consistency with previous behaviour, send onLost callbacks before onAvailable.
processNewlyLostListenRequests(nai);
@@ -7406,9 +7487,9 @@
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
- if (newSatisfier != null) {
+ if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
if (VDBG) log("rematch for " + newSatisfier.toShortString());
- if (previousSatisfier != null) {
+ if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
@@ -7422,7 +7503,7 @@
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ newRequest);
}
- } else {
+ } else if (null != previousSatisfier) {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
+ " request " + previousRequest.requestId);
@@ -7473,7 +7554,11 @@
break;
}
}
- if (bestNetwork != nri.mSatisfier) {
+ if (null == bestNetwork && isDefaultBlocked(nri)) {
+ // Remove default networking if disallowed for managed default requests.
+ bestNetwork = mNoServiceNetwork;
+ }
+ if (nri.getSatisfier() != bestNetwork) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
@@ -7566,7 +7651,7 @@
// if the state has not changed : the source of truth is controlled with
// NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
// called while rematching the individual networks above.
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
lingeredNetworks.add(nai);
}
}
@@ -7593,7 +7678,7 @@
// Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (nai.getLingerExpiry() > 0) {
+ if (nai.getInactivityExpiry() > 0) {
// This network has active linger timers and no requests, but is not
// lingering. Linger it.
//
@@ -7601,7 +7686,7 @@
// and became unneeded due to another network improving its score to the
// point where this network will no longer be able to satisfy any requests
// even if it validates.
- if (updateLingerState(nai, now)) {
+ if (updateInactivityState(nai, now)) {
notifyNetworkLosing(nai, now);
}
} else {
@@ -7878,7 +7963,7 @@
// Notify the requests on this NAI that the network is now lingered.
private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
- final int lingerTime = (int) (nai.getLingerExpiry() - now);
+ final int lingerTime = (int) (nai.getInactivityExpiry() - now);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
}
@@ -7977,7 +8062,7 @@
}
NetworkAgentInfo newDefaultAgent = null;
if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
- newDefaultAgent = getDefaultNetwork();
+ newDefaultAgent = mDefaultRequest.getSatisfier();
if (newDefaultAgent != null) {
intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
newDefaultAgent.networkInfo);
@@ -8025,9 +8110,14 @@
private Network[] getDefaultNetworks() {
ensureRunningOnConnectivityServiceThread();
final ArrayList<Network> defaultNetworks = new ArrayList<>();
- final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ final Set<Integer> activeNetIds = new ArraySet<>();
+ for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+ if (nri.isBeingSatisfied()) {
+ activeNetIds.add(nri.getSatisfier().network().netId);
+ }
+ }
for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+ if (nai.everConnected && (activeNetIds.contains(nai.network().netId) || nai.isVPN())) {
defaultNetworks.add(nai.network);
}
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 916bec2..4dce59f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
@@ -291,8 +292,9 @@
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
- @NonNull TelephonySubscriptionSnapshot snapshot) {
- return new Vcn(vcnContext, subscriptionGroup, config, snapshot);
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback safemodeCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -438,7 +440,12 @@
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot);
+ final VcnSafemodeCallbackImpl safemodeCallback =
+ new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+ final Vcn newInstance =
+ mDeps.newVcn(
+ mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -536,7 +543,7 @@
}
}
- /** Get current configuration list for testing purposes */
+ /** Get current VCNs for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<ParcelUuid, Vcn> getAllVcns() {
synchronized (mLock) {
@@ -638,8 +645,8 @@
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
- // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
- if (mVcns.containsKey(subGroup)) {
+ Vcn vcn = mVcns.get(subGroup);
+ if (vcn != null && vcn.isActive()) {
isVcnManagedNetwork = true;
}
}
@@ -651,4 +658,31 @@
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
}
+
+ /** Callback for signalling when a Vcn has entered Safemode. */
+ public interface VcnSafemodeCallback {
+ /** Called by a Vcn to signal that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+ @NonNull private final ParcelUuid mSubGroup;
+
+ private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+ }
+
+ @Override
+ public void onEnteredSafemode() {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
+
+ notifyAllPolicyListenersLocked();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a43f0c6..e7e9832 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -300,6 +300,7 @@
private static final int MSG_STREAM_DEVICES_CHANGED = 32;
private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
private static final int MSG_REINIT_VOLUMES = 34;
+ private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +700,9 @@
private long mLoweredFromNormalToVibrateTime;
// Array of Uids of valid accessibility services to check if caller is one of them
- private int[] mAccessibilityServiceUids;
private final Object mAccessibilityServiceUidsLock = new Object();
+ @GuardedBy("mAccessibilityServiceUidsLock")
+ private int[] mAccessibilityServiceUids;
// Uid of the active input method service to check if caller is the one or not.
private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -2311,11 +2313,9 @@
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
- boolean hasModifyAudioSettings =
- mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
- caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+ VOL_ADJUST_NORMAL);
}
public void setFastScrollSoundEffectsEnabled(boolean enabled) {
@@ -2442,13 +2442,12 @@
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
return;
}
- final boolean hasModifyAudioSettings =
- mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
+
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+ VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
@@ -2966,13 +2965,11 @@
+ " MODIFY_AUDIO_ROUTING callingPackage=" + callingPackage);
return;
}
- final boolean hasModifyAudioSettings =
- mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED;
+
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), hasModifyAudioSettings);
+ Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
}
private boolean canChangeAccessibilityVolume() {
@@ -3639,8 +3636,7 @@
ensureValidStreamType(streamType);
final boolean isPrivileged =
Binder.getCallingUid() == Process.SYSTEM_UID
- || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED)
+ || callingHasAudioSettingsPermission()
|| (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED);
return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
@@ -4383,13 +4379,10 @@
throw new SecurityException("Should only be called from system process");
}
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
// direction and stream type swap here because the public
// adjustSuggested has a different order than the other methods.
adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
- hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4407,11 +4400,9 @@
new StringBuilder(packageName).append(" uid:").append(uid)
.toString()));
}
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
+
adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
- hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+ hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4423,11 +4414,8 @@
throw new SecurityException("Should only be called from system process");
}
- final boolean hasModifyAudioSettings =
- mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
- == PackageManager.PERMISSION_GRANTED;
setStreamVolume(streamType, index, flags, packageName, packageName, uid,
- hasModifyAudioSettings);
+ hasAudioSettingsPermission(uid, pid));
}
//==========================================================================================
@@ -5393,8 +5381,7 @@
}
boolean checkAudioSettingsPermission(String method) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
- == PackageManager.PERMISSION_GRANTED) {
+ if (callingOrSelfHasAudioSettingsPermission()) {
return true;
}
String msg = "Audio Settings Permission Denial: " + method + " from pid="
@@ -5404,6 +5391,21 @@
return false;
}
+ private boolean callingOrSelfHasAudioSettingsPermission() {
+ return mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean callingHasAudioSettingsPermission() {
+ return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean hasAudioSettingsPermission(int uid, int pid) {
+ return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* Minimum attenuation that can be set for alarms over speaker by an application that
* doesn't have the MODIFY_AUDIO_SETTINGS permission.
@@ -7082,6 +7084,10 @@
case MSG_REINIT_VOLUMES:
onReinitVolumes((String) msg.obj);
break;
+ case MSG_UPDATE_A11Y_SERVICE_UIDS:
+ onUpdateAccessibilityServiceUids();
+ break;
+
}
}
}
@@ -8148,6 +8154,9 @@
+ " FromRestrictions=" + mMicMuteFromRestrictions
+ " FromApi=" + mMicMuteFromApi
+ " from system=" + mMicMuteFromSystemCached);
+ pw.print("\n mAssistantUid="); pw.println(mAssistantUid);
+ pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid);
+ dumpAccessibilityServiceUids(pw);
dumpAudioPolicies(pw);
mDynPolicyLogger.dump(pw);
@@ -8181,6 +8190,19 @@
}
}
+ private void dumpAccessibilityServiceUids(PrintWriter pw) {
+ synchronized (mSupportedSystemUsagesLock) {
+ if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+ pw.println(" Accessibility service Uids:");
+ for (int uid : mAccessibilityServiceUids) {
+ pw.println(" - " + uid);
+ }
+ } else {
+ pw.println(" No accessibility service Uids.");
+ }
+ }
+ }
+
/**
* Audio Analytics ids.
*/
@@ -8484,7 +8506,8 @@
mAccessibilityServiceUids = uids.toArray();
}
}
- AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+ sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+ 0, 0, null, 0);
}
}
@@ -8502,6 +8525,14 @@
}
}
+ private void onUpdateAccessibilityServiceUids() {
+ int[] accessibilityServiceUids;
+ synchronized (mAccessibilityServiceUidsLock) {
+ accessibilityServiceUids = mAccessibilityServiceUids;
+ }
+ AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+ }
+
//==========================================================================================
// Audio policy management
//==========================================================================================
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7..a9a705f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -210,23 +210,23 @@
// network is taken down. This usually only happens to the default network. Lingering ends with
// either the linger timeout expiring and the network being taken down, or the network
// satisfying a request again.
- public static class LingerTimer implements Comparable<LingerTimer> {
+ public static class InactivityTimer implements Comparable<InactivityTimer> {
public final int requestId;
public final long expiryMs;
- public LingerTimer(int requestId, long expiryMs) {
+ public InactivityTimer(int requestId, long expiryMs) {
this.requestId = requestId;
this.expiryMs = expiryMs;
}
public boolean equals(Object o) {
- if (!(o instanceof LingerTimer)) return false;
- LingerTimer other = (LingerTimer) o;
+ if (!(o instanceof InactivityTimer)) return false;
+ InactivityTimer other = (InactivityTimer) o;
return (requestId == other.requestId) && (expiryMs == other.expiryMs);
}
public int hashCode() {
return Objects.hash(requestId, expiryMs);
}
- public int compareTo(LingerTimer other) {
+ public int compareTo(InactivityTimer other) {
return (expiryMs != other.expiryMs) ?
Long.compare(expiryMs, other.expiryMs) :
Integer.compare(requestId, other.requestId);
@@ -269,30 +269,31 @@
*/
public static final int ARG_AGENT_SUCCESS = 1;
- // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+ // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
// was lingering or not.
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
- private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+ private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
- // For fast lookups. Indexes into mLingerTimers by request ID.
- private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+ // For fast lookups. Indexes into mInactivityTimers by request ID.
+ private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
- // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
- // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
- // When the timer fires, all linger state is cleared, and if the network has no requests, it is
- // torn down.
- private WakeupMessage mLingerMessage;
+ // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+ // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+ // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+ // has no requests, it is torn down.
+ private WakeupMessage mInactivityMessage;
- // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
- private long mLingerExpiryMs;
+ // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+ // armed.
+ private long mInactivityExpiryMs;
- // Whether the network is lingering or not. Must be maintained separately from the above because
+ // Whether the network is inactive or not. Must be maintained separately from the above because
// it depends on the state of other networks and requests, which only ConnectivityService knows.
// (Example: we don't linger a network if it would become the best for a NetworkRequest if it
// validated).
- private boolean mLingering;
+ private boolean mInactive;
// This represents the quality of the network with no clear scale.
private int mScore;
@@ -898,17 +899,17 @@
* ConnectivityService when the request is moved to another network with a higher score.
*/
public void lingerRequest(int requestId, long now, long duration) {
- if (mLingerTimerForRequest.get(requestId) != null) {
+ if (mInactivityTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
}
final long expiryMs = now + duration;
- LingerTimer timer = new LingerTimer(requestId, expiryMs);
- if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
- mLingerTimers.add(timer);
- mLingerTimerForRequest.put(requestId, timer);
+ InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+ if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+ mInactivityTimers.add(timer);
+ mInactivityTimerForRequest.put(requestId, timer);
}
/**
@@ -916,23 +917,25 @@
* Returns true if the given requestId was lingering on this network, false otherwise.
*/
public boolean unlingerRequest(int requestId) {
- LingerTimer timer = mLingerTimerForRequest.get(requestId);
+ InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
if (timer != null) {
- if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
- mLingerTimers.remove(timer);
- mLingerTimerForRequest.remove(requestId);
+ if (VDBG) {
+ Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+ }
+ mInactivityTimers.remove(timer);
+ mInactivityTimerForRequest.remove(requestId);
return true;
}
return false;
}
- public long getLingerExpiry() {
- return mLingerExpiryMs;
+ public long getInactivityExpiry() {
+ return mInactivityExpiryMs;
}
- public void updateLingerTimer() {
- long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
- if (newExpiry == mLingerExpiryMs) return;
+ public void updateInactivityTimer() {
+ long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+ if (newExpiry == mInactivityExpiryMs) return;
// Even if we're going to reschedule the timer, cancel it first. This is because the
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -940,49 +943,52 @@
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
// has already been dispatched, rescheduling to some time in the future won't stop it
// from calling its callback immediately.
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
if (newExpiry > 0) {
- mLingerMessage = new WakeupMessage(
+ mInactivityMessage = new WakeupMessage(
mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
this /* obj (NetworkAgentInfo) */);
- mLingerMessage.schedule(newExpiry);
+ mInactivityMessage.schedule(newExpiry);
}
- mLingerExpiryMs = newExpiry;
+ mInactivityExpiryMs = newExpiry;
}
- public void linger() {
- mLingering = true;
+ public void setInactive() {
+ mInactive = true;
}
- public void unlinger() {
- mLingering = false;
+ public void unsetInactive() {
+ mInactive = false;
}
public boolean isLingering() {
- return mLingering;
+ return mInactive;
}
- public void clearLingerState() {
- if (mLingerMessage != null) {
- mLingerMessage.cancel();
- mLingerMessage = null;
+ public void clearInactivityState() {
+ if (mInactivityMessage != null) {
+ mInactivityMessage.cancel();
+ mInactivityMessage = null;
}
- mLingerTimers.clear();
- mLingerTimerForRequest.clear();
- updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
- mLingering = false;
+ mInactivityTimers.clear();
+ mInactivityTimerForRequest.clear();
+ // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+ updateInactivityTimer();
+ mInactive = false;
}
- public void dumpLingerTimers(PrintWriter pw) {
- for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+ public void dumpInactivityTimers(PrintWriter pw) {
+ for (InactivityTimer timer : mInactivityTimers) {
+ pw.println(timer);
+ }
}
/**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d956ba3..e8062ae 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -203,6 +203,7 @@
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
private final Ikev2SessionCreator mIkev2SessionCreator;
+ private final UserManager mUserManager;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +278,10 @@
return LocalServices.getService(DeviceIdleInternal.class);
}
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return VpnConfig.getIntentForStatusPanel(context);
+ }
+
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -405,6 +410,7 @@
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
+ mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1431,7 +1437,7 @@
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
- users = UserManager.get(mContext).getAliveUsers();
+ users = mUserManager.getAliveUsers();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1515,7 +1521,7 @@
*/
public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1543,7 +1549,7 @@
*/
public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1768,7 +1774,7 @@
private void prepareStatusIntent() {
final long token = Binder.clearCallingIdentity();
try {
- mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+ mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1968,8 +1974,7 @@
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
- final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserId);
+ final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2004,9 +2009,8 @@
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
@Nullable Network underlying, @NonNull LinkProperties egress) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserId);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ UserInfo user = mUserManager.getUserInfo(mUserId);
+ if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index afc97c7..dac94f6 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -27,6 +27,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.text.FontConfig;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.Slog;
@@ -39,7 +40,6 @@
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Instant;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -116,7 +116,7 @@
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
* corresponding font files in {@link #mPreinstalledFontDirs}.
*/
- private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
+ private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
FsverityUtil fsverityUtil) {
@@ -205,7 +205,7 @@
*/
public void update(List<FontUpdateRequest> requests) throws SystemFontException {
// Backup the mapping for rollback.
- HashMap<String, FontFileInfo> backupMap = new HashMap<>(mFontFileInfoMap);
+ ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
long backupLastModifiedDate = mLastModifiedDate;
boolean success = false;
try {
@@ -464,7 +464,7 @@
}
Map<String, File> getFontFileMap() {
- Map<String, File> map = new HashMap<>();
+ Map<String, File> map = new ArrayMap<>();
for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
map.put(entry.getKey(), entry.getValue().getFile());
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..d8e124a0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -300,45 +299,6 @@
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
"com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
- /**
- * Debug flag for overriding runtime {@link SystemProperties}.
- */
- @AnyThread
- private static final class DebugFlag {
- private static final Object LOCK = new Object();
- private final String mKey;
- private final boolean mDefaultValue;
- @GuardedBy("LOCK")
- private boolean mValue;
-
- public DebugFlag(String key, boolean defaultValue) {
- mKey = key;
- mDefaultValue = defaultValue;
- mValue = SystemProperties.getBoolean(key, defaultValue);
- }
-
- void refresh() {
- synchronized (LOCK) {
- mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
- }
- }
-
- boolean value() {
- synchronized (LOCK) {
- return mValue;
- }
- }
- }
-
- /**
- * Debug flags that can be overridden using "adb shell setprop <key>"
- * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
- */
- private static final class DebugFlags {
- static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
- new DebugFlag("debug.optimize_startinput", false);
- }
-
@UserIdInt
private int mLastSwitchUserId;
@@ -3687,12 +3647,9 @@
}
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
- || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+ } else {
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else {
- res = InputBindResult.NO_EDITOR;
}
} else {
res = InputBindResult.NULL_EDITOR_INFO;
@@ -5467,10 +5424,6 @@
@BinderThread
@ShellCommandResult
private int onCommandWithSystemIdentity(@Nullable String cmd) {
- if ("refresh_debug_properties".equals(cmd)) {
- return refreshDebugProperties();
- }
-
if ("get-last-switch-user-id".equals(cmd)) {
return mService.getLastSwitchUserId(this);
}
@@ -5505,13 +5458,6 @@
}
@BinderThread
- @ShellCommandResult
- private int refreshDebugProperties() {
- DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
- return ShellCommandResult.SUCCESS;
- }
-
- @BinderThread
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 02a36dc..f646d5d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -220,7 +220,8 @@
*/
@VisibleForTesting
public Context getSettingsContext(int displayId) {
- if (mSettingsContext == null) {
+ // TODO(b/178462039): Cover the case when IME is moved to another ImeContainer.
+ if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
final Context systemUiContext = ActivityThread.currentActivityThread()
.createSystemUiContext(displayId);
final Context windowContext = systemUiContext.createWindowContext(
@@ -229,11 +230,6 @@
windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
mSwitchingDialogToken = mSettingsContext.getWindowContextToken();
}
- // TODO(b/159767464): register the listener to another display again if window token is not
- // yet created.
- if (mSettingsContext.getDisplayId() != displayId) {
- mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
- }
return mSettingsContext;
}
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
similarity index 98%
rename from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
rename to services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d74..b5746bb 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.utils.eventlog;
+package com.android.server.location.eventlog;
import android.os.SystemClock;
import android.util.TimeUtils;
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
index b8b54b3..8d73518 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java
@@ -31,7 +31,7 @@
import android.os.Build;
import android.os.PowerManager.LocationPowerSaveMode;
-import com.android.server.utils.eventlog.LocalEventLog;
+import com.android.server.location.eventlog.LocalEventLog;
/** In memory event log for location events. */
public class LocationEventLog extends LocalEventLog {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dc..39ed7e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
package com.android.server.net;
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
import android.annotation.NonNull;
import android.net.Network;
import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@
public abstract void resetUserState(int userId);
/**
- * Figure out if networking is blocked for a given set of conditions.
- *
- * This is used by ConnectivityService via passing stale copies of conditions, so it must not
- * take any locks.
- *
- * @param uid The target uid.
- * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
- * @param isNetworkMetered True if the network is metered.
- * @param isBackgroundRestricted True if data saver is enabled.
- *
- * @return true if networking is blocked for the UID under the specified conditions.
- */
- public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
- boolean isBackgroundRestricted) {
- // Log of invoking internal function is disabled because it will be called very
- // frequently. And metrics are unlikely needed on this method because the callers are
- // external and this method doesn't take any locks or perform expensive operations.
- return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted, null);
- }
-
- /**
* Informs that an appId has been added or removed from the temp-powersave-allowlist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ca21640..b99a552 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -5402,6 +5402,17 @@
}
@Override
+ public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+ boolean isNetworkMetered, boolean isBackgroundRestricted) {
+ mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+ // Log of invoking this function is disabled because it will be called very frequently. And
+ // metrics are unlikely needed on this method because the callers are external and this
+ // method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ @Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
final int uidRules;
@@ -5410,9 +5421,9 @@
uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
isBackgroundRestricted = mRestrictBackground;
}
- //TODO(b/177490332): The logic here might not be correct because it doesn't consider
- // RULE_REJECT_METERED condition. And it could be replaced by
- // isUidNetworkingBlockedInternal().
+ // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+ // RULE_REJECT_METERED condition. And it could be replaced by
+ // isUidNetworkingBlockedInternal().
return isBackgroundRestricted
&& !hasRule(uidRules, RULE_ALLOW_METERED)
&& !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 461d519..619fc4e 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -23,8 +23,8 @@
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index de77372..18c689f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,8 +21,8 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f99..66ea554 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@
/**
* Fetch or calculate checksums for the collection of files.
*
- * @param filesToChecksum split name, null for base and File to fetch checksums for
- * @param optional mask to fetch readily available checksums
- * @param required mask to forcefully calculate if not available
- * @param installerPackageName package name of the installer of the packages
- * @param trustedInstallers array of certificate to trust, two specific cases:
- * null - trust anybody,
- * [] - trust nobody.
- * @param statusReceiver to receive the resulting checksums
+ * @param filesToChecksum split name, null for base and File to fetch checksums for
+ * @param optional mask to fetch readily available checksums
+ * @param required mask to forcefully calculate if not available
+ * @param installerPackageName package name of the installer of the packages
+ * @param trustedInstallers array of certificate to trust, two specific cases:
+ * null - trust anybody,
+ * [] - trust nobody.
+ * @param onChecksumsReadyListener to receive the resulting checksums
*/
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
@Checksum.Type int optional,
@Checksum.Type int required,
@Nullable String installerPackageName,
@Nullable Certificate[] trustedInstallers,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector) {
List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@
}
long startTime = SystemClock.uptimeMillis();
- processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
- startTime);
+ processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+ injector, startTime);
}
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
List<Map<Integer, ApkChecksum>> result,
@Checksum.Type int required,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector,
long startTime) {
final boolean timeout =
@@ -350,7 +349,7 @@
// Not ready, come back later.
injector.getHandler().postDelayed(() -> {
processRequiredChecksums(filesToChecksum, result, required,
- statusReceiver, injector, startTime);
+ onChecksumsReadyListener, injector, startTime);
}, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
return;
}
@@ -363,13 +362,9 @@
}
}
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_CHECKSUMS,
- allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
try {
- statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
+ onChecksumsReadyListener.onChecksumsReady(allChecksums);
+ } catch (RemoteException e) {
Slog.w(TAG, e);
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 0000000..a32e107
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+ @IntDef({
+ Direction.TO_PARENT,
+ Direction.TO_PROFILE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Direction {
+ int TO_PARENT = 0;
+ int TO_PROFILE = 1;
+ }
+
+ /** The intent filter that's used */
+ public final IntentFilter filter;
+
+ /**
+ * The flags related to the forwarding, e.g.
+ * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+ * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+ */
+ public final int flags;
+
+ /**
+ * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+ * {@link Direction#TO_PROFILE}.
+ */
+ public final @Direction int direction;
+
+ /**
+ * Whether this cross profile intent filter would allow personal data to be shared into
+ * the work profile. If this is {@code true}, this intent filter should be only added to
+ * the profile if the admin does not enable
+ * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+ */
+ public final boolean letsPersonalDataIntoProfile;
+
+ private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+ @Direction int direction, boolean letsPersonalDataIntoProfile) {
+ this.filter = requireNonNull(filter);
+ this.flags = flags;
+ this.direction = direction;
+ this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ static final class Builder {
+ private IntentFilter mFilter = new IntentFilter();
+ private int mFlags;
+ private @Direction int mDirection;
+ private boolean mLetsPersonalDataIntoProfile;
+
+ Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+ mDirection = direction;
+ mFlags = flags;
+ mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ Builder addAction(String action) {
+ mFilter.addAction(action);
+ return this;
+ }
+
+ Builder addCategory(String category) {
+ mFilter.addCategory(category);
+ return this;
+ }
+
+ Builder addDataType(String type) {
+ try {
+ mFilter.addDataType(type);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ // ignore
+ }
+ return this;
+ }
+
+ Builder addDataScheme(String scheme) {
+ mFilter.addDataScheme(scheme);
+ return this;
+ }
+
+ DefaultCrossProfileIntentFilter build() {
+ return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+ mLetsPersonalDataIntoProfile);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 0000000..3019439
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+ private DefaultCrossProfileIntentFiltersUtils() {
+ }
+
+ // Intents from profile to parent user
+ /** Emergency call intent with mime type is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Emergency call intent with data schemes is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /**
+ * Dial intent with no data scheme or type can be handled by either managed profile or its
+ * parent user.
+ */
+ private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .build();
+
+ /** Pressing the call button can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_BUTTON)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** SMS and MMS are exclusively handled by the primary user. */
+ private static final DefaultCrossProfileIntentFilter SMS_MMS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_VIEW)
+ .addAction(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("sms")
+ .addDataScheme("smsto")
+ .addDataScheme("mms")
+ .addDataScheme("mmsto")
+ .build();
+
+ /** Mobile network settings is always shown in the primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ MOBILE_NETWORK_SETTINGS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+ .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** HOME intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter HOME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_HOME)
+ .build();
+
+ /** Get content can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Open document intent can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_OPEN_DOCUMENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick for any data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick without data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Speech recognition can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(ACTION_RECOGNIZE_SPEECH)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Media capture can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+ .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Alarm setting can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter SET_ALARM =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(AlarmClock.ACTION_SET_ALARM)
+ .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+ .addAction(AlarmClock.ACTION_SET_TIMER)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ // Intents from parent to profile user
+
+ /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+ private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_SEND)
+ .addAction(Intent.ACTION_SEND_MULTIPLE)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** USB devices attached can get forwarded to the profile. */
+ private static final DefaultCrossProfileIntentFilter
+ USB_DEVICE_ATTACHED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+ .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+ return Arrays.asList(
+ EMERGENCY_CALL_MIME,
+ EMERGENCY_CALL_DATA,
+ DIAL_MIME,
+ DIAL_DATA,
+ DIAL_RAW,
+ CALL_BUTTON,
+ SMS_MMS,
+ SET_ALARM,
+ MEDIA_CAPTURE,
+ RECOGNIZE_SPEECH,
+ ACTION_PICK_RAW,
+ ACTION_PICK_DATA,
+ OPEN_DOCUMENT,
+ GET_CONTENT,
+ USB_DEVICE_ATTACHED,
+ ACTION_SEND,
+ HOME,
+ MOBILE_NETWORK_SETTINGS);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f444ed6..498c314 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -177,6 +177,7 @@
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -5523,19 +5524,19 @@
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional,
@Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId) {
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
- statusReceiver, userId, mInjector.getBackgroundExecutor(),
+ onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
mInjector.getBackgroundHandler());
}
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
- @Checksum.Type int optional,
- @Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
- @NonNull Handler handler) {
+ @Checksum.Type int optional, @Checksum.Type int required,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+ @NonNull Executor executor, @NonNull Handler handler) {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(executor);
Objects.requireNonNull(handler);
@@ -5571,7 +5572,7 @@
() -> mInjector.getIncrementalManager(),
() -> mPmInternal);
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
- trustedCerts, statusReceiver, injector);
+ trustedCerts, onChecksumsReadyListener, injector);
});
}
@@ -27110,12 +27111,14 @@
ps.setStatesOnCrashOrAnr();
}
+ @Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler) {
requestChecksumsInternal(packageName, includeSplits, optional, required,
- trustedInstallers, statusReceiver, userId, executor, handler);
+ trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
}
@Override
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c91..8c5084a 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
import android.app.admin.SecurityLog;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -39,7 +33,6 @@
import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -109,26 +102,23 @@
// Capturing local loggingInfo to still log even if hash was invalidated.
try {
pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
- new IntentSender((IIntentSender) new IIntentSender.Stub() {
+ new IOnChecksumsReadyListener.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- processChecksums(loggingInfo, intent);
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ processChecksums(loggingInfo, checksums);
}
- }), context.getUserId(), mExecutor, this);
+ }, context.getUserId(),
+ mExecutor, this);
} catch (Throwable t) {
Slog.e(TAG, "requestChecksums() failed", t);
enqueueProcessChecksum(loggingInfo, null);
}
}
- void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
- Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
- ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
- ApkChecksum[].class);
-
- for (ApkChecksum checksum : checksums) {
+ void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+ for (int i = 0, size = checksums.size(); i < size; ++i) {
+ ApkChecksum checksum = checksums.get(i);
if (checksum.getType() == CHECKSUM_TYPE) {
processChecksum(loggingInfo, checksum.getValue());
return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca4..e5a70c3 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@
import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@
if (!changed) {
return false;
}
- if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+ if (!BundleUtils.isEmpty(restrictions)) {
mUserRestrictions.put(userId, restrictions);
} else {
mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d68..c17d2e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@
* Gets all {@link UserInfo UserInfos}.
*/
public abstract @NonNull UserInfo[] getUserInfos();
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}.
+ */
+ public abstract void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a807777..753f22d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
import android.os.UserManager.EnforcingUser;
import android.os.UserManager.QuietModeFlag;
import android.os.storage.StorageManager;
+import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@
final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
- if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+ if (BundleUtils.isEmpty(global) && local.isEmpty()) {
// Common case first.
return baseRestrictions;
}
- final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+ final Bundle effective = BundleUtils.clone(baseRestrictions);
UserRestrictionsUtils.merge(effective, global);
UserRestrictionsUtils.merge(effective, local.mergeAll());
@@ -2105,7 +2107,7 @@
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
- return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+ return BundleUtils.clone(getEffectiveUserRestrictions(userId));
}
@Override
@@ -2129,7 +2131,7 @@
synchronized (mRestrictionsLock) {
// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
// a copy.
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userId));
newRestrictions.putBoolean(key, value);
@@ -2727,7 +2729,7 @@
if (userVersion < 7) {
// Previously only one user could enforce global restrictions, now it is per-user.
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+ if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
&& mDeviceOwnerUserId != UserHandle.USER_NULL) {
mDevicePolicyGlobalUserRestrictions.updateRestrictions(
mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@
t.traceBegin("PM.onNewUserCreated-" + userId);
mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
t.traceEnd();
+ applyDefaultUserSettings(userTypeDetails, userId);
+ setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
if (preCreate) {
// Must start user (which will be stopped right away, through
// UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@
return userInfo;
}
+ private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+ final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+ final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+ if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+ return;
+ }
+
+ final int systemSettingsSize = systemSettings.size();
+ final String[] systemSettingsArray = systemSettings.keySet().toArray(
+ new String[systemSettingsSize]);
+ for (int i = 0; i < systemSettingsSize; i++) {
+ final String setting = systemSettingsArray[i];
+ if (!Settings.System.putStringForUser(
+ mContext.getContentResolver(), setting, systemSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+ }
+ }
+
+ final int secureSettingsSize = secureSettings.size();
+ final String[] secureSettingsArray = secureSettings.keySet().toArray(
+ new String[secureSettingsSize]);
+ for (int i = 0; i < secureSettingsSize; i++) {
+ final String setting = secureSettingsArray[i];
+ if (!Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), setting, secureSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+ }
+ }
+ }
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+ */
+ private void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+ Bundle profileRestrictions, @UserIdInt int parentUserId) {
+ if (profileDetails == null || !profileDetails.isProfile()) {
+ return;
+ }
+ final List<DefaultCrossProfileIntentFilter> filters =
+ profileDetails.getDefaultCrossProfileIntentFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+ final boolean disallowSharingIntoProfile =
+ profileRestrictions.getBoolean(
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+ /* defaultValue = */ false);
+ final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+ for (int i = 0; i < size; i++) {
+ final DefaultCrossProfileIntentFilter filter =
+ profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+ if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+ continue;
+ }
+ if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+ filter.flags);
+ } else {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+ filter.flags);
+ }
+ }
+ }
+
/**
* Finds and converts a previously pre-created user into a regular user, if possible.
*
@@ -5462,6 +5539,15 @@
return allInfos;
}
}
+
+ @Override
+ public void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+ final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+ UserManagerService.this.setDefaultCrossProfileIntentFilters(
+ profileUserId, userTypeDetails, restrictions, parentUserId);
+ }
}
/**
@@ -5726,8 +5812,8 @@
// merge existing base restrictions with the new type's default restrictions
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userInfo.id));
UserRestrictionsUtils.merge(newRestrictions,
newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff40..ff90043 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@
return in != null ? in : new Bundle();
}
- public static boolean isEmpty(@Nullable Bundle in) {
- return (in == null) || (in.size() == 0);
- }
-
/**
* Returns {@code true} if given bundle is not null and contains {@code true} for a given
* restriction.
@@ -396,17 +393,6 @@
return in != null && in.getBoolean(restriction);
}
- /**
- * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
- * bundle.
- *
- * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
- * {@link Bundle#EMPTY})
- */
- public static @NonNull Bundle clone(@Nullable Bundle in) {
- return (in != null) ? new Bundle(in) : new Bundle();
- }
-
public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
Objects.requireNonNull(dest);
Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@
if (a == b) {
return true;
}
- if (isEmpty(a)) {
- return isEmpty(b);
+ if (BundleUtils.isEmpty(a)) {
+ return BundleUtils.isEmpty(b);
}
- if (isEmpty(b)) {
+ if (BundleUtils.isEmpty(b)) {
return false;
}
for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9..17ce386 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@
import android.os.UserManager;
import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@
*/
private final @Nullable Bundle mDefaultRestrictions;
+ /**
+ * List of {@link android.provider.Settings.System} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSystemSettings;
+
+ /**
+ * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSecureSettings;
+
+ /**
+ * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+ * profiles.
+ */
+ private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
// Fields for profiles only, controlling the nature of their badges.
// All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@
int iconBadge, int badgePlain, int badgeNoBackground,
@Nullable int[] badgeLabels, @Nullable int[] badgeColors,
@Nullable int[] darkThemeBadgeColors,
- @Nullable Bundle defaultRestrictions) {
+ @Nullable Bundle defaultRestrictions,
+ @Nullable Bundle defaultSystemSettings,
+ @Nullable Bundle defaultSecureSettings,
+ @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@
this.mBaseType = baseType;
this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
this.mDefaultRestrictions = defaultRestrictions;
+ this.mDefaultSystemSettings = defaultSystemSettings;
+ this.mDefaultSecureSettings = defaultSecureSettings;
+ this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
this.mIconBadge = iconBadge;
this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@
return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
}
- /** Returns a Bundle representing the default user restrictions. */
+ /** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
- return UserRestrictionsUtils.clone(mDefaultRestrictions);
+ return BundleUtils.clone(mDefaultRestrictions);
}
/** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@
UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
}
+ /** Returns a {@link Bundle} representing the default system settings. */
+ @NonNull Bundle getDefaultSystemSettings() {
+ return BundleUtils.clone(mDefaultSystemSettings);
+ }
+
+ /** Returns a {@link Bundle} representing the default secure settings. */
+ @NonNull Bundle getDefaultSecureSettings() {
+ return BundleUtils.clone(mDefaultSecureSettings);
+ }
+
+ /** Returns a list of default cross profile intent filters. */
+ @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+ return mDefaultCrossProfileIntentFilters != null
+ ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+ : Collections.emptyList();
+ }
+
+
/** Dumps details of the UserTypeDetails. Do not parse this. */
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@
private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
private int mDefaultUserInfoPropertyFlags = 0;
private @Nullable Bundle mDefaultRestrictions = null;
+ private @Nullable Bundle mDefaultSystemSettings = null;
+ private @Nullable Bundle mDefaultSecureSettings = null;
+ private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+ null;
private boolean mEnabled = true;
private int mLabel = Resources.ID_NULL;
private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@
return this;
}
+ public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+ mDefaultSystemSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+ mDefaultSecureSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultCrossProfileIntentFilters(
+ @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+ mDefaultCrossProfileIntentFilters = intentFilters;
+ return this;
+ }
+
@UserInfoFlag int getBaseType() {
return mBaseType;
}
@@ -425,17 +491,28 @@
Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
"UserTypeDetails " + mName + " has badge but no badgeColors.");
}
+ if (!isProfile()) {
+ Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+ || mDefaultCrossProfileIntentFilters.isEmpty(),
+ "UserTypeDetails %s has a non empty "
+ + "defaultCrossProfileIntentFilters", mName);
+ }
return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
- mDefaultRestrictions);
+ mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+ mDefaultCrossProfileIntentFilters);
}
private boolean hasBadge() {
return mIconBadge != Resources.ID_NULL;
}
+ private boolean isProfile() {
+ return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+ }
+
// TODO(b/143784345): Refactor this when we clean up UserInfo.
private boolean hasValidBaseType() {
return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf60..6aac0b2 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@
com.android.internal.R.color.profile_badge_1_dark,
com.android.internal.R.color.profile_badge_2_dark,
com.android.internal.R.color.profile_badge_3_dark)
- .setDefaultRestrictions(null);
+ .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+ .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+ .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
}
/**
@@ -256,12 +258,35 @@
return restrictions;
}
+ private static Bundle getDefaultManagedProfileRestrictions() {
+ final Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+ return restrictions;
+ }
+
+ private static Bundle getDefaultManagedProfileSecureSettings() {
+ // Only add String values to the bundle, settings are written as Strings eventually
+ final Bundle settings = new Bundle();
+ settings.putString(
+ android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+ settings.putString(
+ android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+ return settings;
+ }
+
+ private static List<DefaultCrossProfileIntentFilter>
+ getDefaultManagedCrossProfileIntentFilter() {
+ return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+ }
+
/**
* Reads the given xml parser to obtain device user-type customization, and updates the given
* map of {@link UserTypeDetails.Builder}s accordingly.
* <p>
* The xml file can specify the attributes according to the set... methods below.
*/
+ // TODO(b/176973369): Add parsing logic to support custom settings/filters
+ // in config_user_types.xml
@VisibleForTesting
static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
XmlResourceParser parser) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e..bc81961 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
import java.io.File;
import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@
handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
}
});
+
mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
new StateCallback() {
@Override
@@ -3136,7 +3138,7 @@
private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
final int res = applyKeyguardOcclusionChange();
if (res != 0) return res;
- if (keyguardGoingAway) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d6440..c77e266 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1158,7 +1158,7 @@
* @param startTime the start time of the animation in uptime milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+ void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c79..a95628f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
@@ -398,7 +399,7 @@
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
- if (mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index a82f239..5ec527a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
@@ -37,6 +38,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents an single instance of a VCN.
@@ -82,10 +84,19 @@
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
+ /**
+ * Causes this VCN to immediately enter Safemode.
+ *
+ * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+ * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+ */
+ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
+ @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -94,14 +105,33 @@
@NonNull private VcnConfig mConfig;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ /**
+ * Whether this Vcn instance is active and running.
+ *
+ * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+ * shut down or has entered safe mode.
+ *
+ * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+ * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+ * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+ * Looper.
+ */
+ // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+ private final AtomicBoolean mIsActive = new AtomicBoolean(true);
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
- @NonNull TelephonySubscriptionSnapshot snapshot) {
- this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies());
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ config,
+ snapshot,
+ vcnSafemodeCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -110,10 +140,13 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mVcnSafemodeCallback =
+ Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
@@ -143,6 +176,11 @@
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
+ /** Synchronously checks whether this Vcn is active. */
+ public boolean isActive() {
+ return mIsActive.get();
+ }
+
/** Get current Gateways for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Set<VcnGatewayConnection> getVcnGatewayConnections() {
@@ -160,7 +198,7 @@
@Override
public void handleMessage(@NonNull Message msg) {
- if (!mIsRunning) {
+ if (!isActive()) {
return;
}
@@ -177,6 +215,9 @@
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
+ case MSG_CMD_ENTER_SAFEMODE:
+ handleEnterSafemode();
+ break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
@@ -198,7 +239,13 @@
gatewayConnection.teardownAsynchronously();
}
- mIsRunning = false;
+ mIsActive.set(false);
+ }
+
+ private void handleEnterSafemode() {
+ handleTeardown();
+
+ mVcnSafemodeCallback.onEnteredSafemode();
}
private void handleNetworkRequested(
@@ -233,7 +280,8 @@
mVcnContext,
mSubscriptionGroup,
mLastSnapshot,
- gatewayConnectionConfig);
+ gatewayConnectionConfig,
+ new VcnGatewayStatusCallbackImpl());
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
@@ -242,7 +290,7 @@
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
- if (mIsRunning) {
+ if (isActive()) {
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
@@ -271,6 +319,20 @@
return 52;
}
+ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public interface VcnGatewayStatusCallback {
+ /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ @Override
+ public void onEnteredSafemode() {
+ sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+ }
+ }
+
/** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -279,9 +341,14 @@
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- VcnGatewayConnectionConfig connectionConfig) {
+ VcnGatewayConnectionConfig connectionConfig,
+ VcnGatewayStatusCallback gatewayStatusCallback) {
return new VcnGatewayConnection(
- vcnContext, subscriptionGroup, snapshot, connectionConfig);
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 853bb43..9ecdf1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,7 +61,6 @@
import android.util.ArraySet;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State;
@@ -69,6 +68,7 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import java.io.IOException;
import java.net.Inet4Address;
@@ -403,15 +403,13 @@
@NonNull
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
- @NonNull private final Object mLock = new Object();
-
- @GuardedBy("mLock")
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+ @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -487,8 +485,15 @@
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnGatewayConnectionConfig connectionConfig) {
- this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies());
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -497,16 +502,17 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
@NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+ mGatewayStatusCallback =
+ Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
- synchronized (mLock) {
- mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
- }
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
@@ -577,13 +583,10 @@
*/
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
Objects.requireNonNull(snapshot, "Missing snapshot");
+ mVcnContext.ensureRunningOnLooperThread();
- // Vcn is the only user of this method and runs on the same Thread, but lock around
- // mLastSnapshot to be technically correct.
- synchronized (mLock) {
- mLastSnapshot = snapshot;
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
- }
+ mLastSnapshot = snapshot;
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ce951b8..771b712 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -975,7 +975,8 @@
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
- packageName, enterAnim, exitAnim, null, null);
+ packageName, enterAnim, exitAnim, null, null,
+ r.mOverrideTaskTransition);
}
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 36c5037..262505f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,7 +201,6 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -273,7 +270,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -682,6 +678,7 @@
boolean mRequestForceTransition;
boolean mEnteringAnimation;
+ boolean mOverrideTaskTransition;
boolean mAppStopped;
// A hint to override the window specified rotation animation, or -1 to use the window specified
@@ -1627,6 +1624,8 @@
if (rotationAnimation >= 0) {
mRotationAnimationHint = rotationAnimation;
}
+
+ mOverrideTaskTransition = options.getOverrideTaskTransition();
}
ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -3997,7 +3996,8 @@
pendingOptions.getCustomEnterResId(),
pendingOptions.getCustomExitResId(),
pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener());
+ pendingOptions.getAnimationFinishedListener(),
+ pendingOptions.getOverrideTaskTransition());
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -6775,20 +6775,6 @@
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
- // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
- // size compat mode.
- if (providesMaxBounds()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
- + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
- + "mode %s", getUid(),
- resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
- !matchParentBounds(), inSizeCompatMode());
- }
- resolvedConfig.windowConfiguration
- .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
- }
}
/**
@@ -6972,19 +6958,6 @@
return super.getBounds();
}
- @Override
- public boolean providesMaxBounds() {
- // System and SystemUI should always be able to access the physical display bounds,
- // so do not provide it with the overridden maximum bounds.
- // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
- if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
- getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
- return false;
- }
- // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
- return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
- }
-
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d846c3a..79f8229 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1814,8 +1814,7 @@
}
private Task computeTargetTask() {
- if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
- && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if (mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
// A new task should be created instead of using existing one.
return null;
} else if (mSourceRecord != null) {
@@ -2505,7 +2504,9 @@
// If bring to front is requested, and no result is requested and we have not been given
// an explicit task to launch in to, and we can find a task that was started with this
// same component, then instead of launching bring that one to the front.
- putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+ putIntoExistingTask &= !isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)
+ ? (mInTask == null && mStartActivity.resultTo == null)
+ : (mInTask == null);
ActivityRecord intentActivity = null;
if (putIntoExistingTask) {
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 90070c8..5b685b4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
@@ -257,6 +258,7 @@
private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+ private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
private int mLastClipRevealMaxTranslation;
@@ -266,6 +268,7 @@
private final boolean mLowRamRecentsEnabled;
private final int mDefaultWindowAnimationStyleResId;
+ private boolean mOverrideTaskTransition;
private RemoteAnimationController mRemoteAnimationController;
@@ -445,7 +448,7 @@
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
if (mRemoteAnimationController != null) {
- mRemoteAnimationController.goodToGo();
+ mRemoteAnimationController.goodToGo(transit);
}
return redoLayout;
}
@@ -508,6 +511,11 @@
mListeners.remove(listener);
}
+ void registerKeygaurdExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ mKeyguardExitAnimationStartListener = listener;
+ }
+
public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -971,7 +979,8 @@
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
boolean freeform, WindowContainer container) {
- if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) {
+ if (mNextAppTransitionOverrideRequested
+ && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
}
@@ -1175,7 +1184,8 @@
}
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
- IRemoteCallback startedCallback, IRemoteCallback endedCallback) {
+ IRemoteCallback startedCallback, IRemoteCallback endedCallback,
+ boolean overrideTaskTransaction) {
if (canOverridePendingAppTransition()) {
clear();
mNextAppTransitionOverrideRequested = true;
@@ -1185,6 +1195,7 @@
postAnimationCallback();
mNextAppTransitionCallback = startedCallback;
mAnimationFinishedCallback = endedCallback;
+ mOverrideTaskTransition = overrideTaskTransaction;
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 582aeb3..4575cf7 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@
private final DisplayContent mDisplayContent;
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+ private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
@@ -437,10 +438,14 @@
return adapter;
}
}
- if (mRemoteAnimationDefinition == null) {
- return null;
+ if (mRemoteAnimationDefinition != null) {
+ final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+ transit, activityTypes);
+ if (adapter != null) {
+ return adapter;
+ }
}
- return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+ return null;
}
/**
@@ -709,6 +714,13 @@
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
+ for (int i = 0; i < openingApps.size(); ++i) {
+ openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+ }
+ for (int i = 0; i < closingApps.size(); ++i) {
+ closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+ }
+
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 02e281f5..61fe023 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,7 +25,7 @@
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
@@ -35,7 +35,7 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.ViewRootImpl.computeWindowBounds;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -1074,7 +1074,7 @@
rect.bottom = rect.top + getStatusBarHeight(displayFrames);
};
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
- mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider);
+ mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
break;
case TYPE_NAVIGATION_BAR:
@@ -1101,7 +1101,7 @@
(displayFrames, windowState, inOutFrame) ->
inOutFrame.set(windowState.getFrame()));
- mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
+ mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
(displayFrames, windowState, inOutFrame) -> {
inOutFrame.top -= mBottomGestureAdditionalInset;
});
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+ if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072..392f27e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
- void goodToGo() {
+ void goodToGo(@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+ // TODO(bc-unlock): Create the remote non app animation targets (if any)
+ final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
- mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
- mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+ wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -274,6 +279,7 @@
private void setRunningRemoteAnimation(boolean running) {
final int pid = mRemoteAnimationAdapter.getCallingPid();
final int uid = mRemoteAnimationAdapter.getCallingUid();
+
if (pid == 0) {
throw new RuntimeException("Calling pid of remote animation was null");
}
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 6df4536..6567195 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -253,6 +253,20 @@
throw new SecurityException(msg);
}
+ // Check if the caller is allowed to override any app transition animation.
+ final boolean overrideTaskTransition = options.getOverrideTaskTransition();
+ if (aInfo != null && overrideTaskTransition) {
+ final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission(
+ START_TASKS_FROM_RECENTS, callingPid, callingUid);
+ if (startTasksFromRecentsPerm != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with overrideTaskTransition=true";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
// Check permission for remote animations
final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
if (adapter != null && supervisor.mService.checkPermission(
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 30af0ed..e44a028 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -921,10 +919,8 @@
return;
}
- if (isLeafTask()) {
- // This task is going away, so save the last state if necessary.
- saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
- }
+ // This task is going away, so save the last state if necessary.
+ saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
// TODO: VI what about activity?
final boolean isVoiceSession = voiceSession != null;
@@ -2459,14 +2455,16 @@
/**
* Saves launching state if necessary so that we can launch the activity to its latest state.
- * It only saves state if this task has been shown to user and it's in fullscreen or freeform
- * mode on freeform displays.
*/
private void saveLaunchingStateIfNeeded() {
saveLaunchingStateIfNeeded(getDisplayContent());
}
private void saveLaunchingStateIfNeeded(DisplayContent display) {
+ if (!isLeafTask()) {
+ return;
+ }
+
if (!getHasBeenVisible()) {
// Not ever visible to user.
return;
@@ -2867,16 +2865,6 @@
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
- // Since the task is letterboxed due to mismatched orientation against its parent,
- // sandbox max bounds to the app bounds.
- if (!outBounds.isEmpty()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
- + "orientation with parent, to %s vs DisplayArea %s", outBounds,
- getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
- }
- getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
- }
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4185407..63732d8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -905,6 +906,13 @@
}
}
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final ActivityRecord activity = getTopMostActivity();
+ return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+ }
+
SurfaceControl getSplitScreenDividerAnchor() {
return mSplitScreenDividerAnchor;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23..98eb11f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@
dc.mWallpaperController.startWallpaperAnimation(anim);
}
}
- }
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
dc.startKeyguardExitOnNonAppWindows(
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..eed3299 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -26,9 +26,11 @@
import android.os.IBinder;
import android.view.Display;
import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
@@ -154,6 +156,21 @@
}
/**
+ * An interface to be notified when keyguard exit animation should start.
+ */
+ public interface KeyguardExitAnimationStartListener {
+ /**
+ * Called when keyguard exit animation should start.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ IRemoteAnimationFinishedCallback finishedCallback);
+ }
+
+ /**
* An interface to be notified about hardware keyboard status.
*/
public interface OnHardKeyboardStatusChangeListener {
@@ -372,6 +389,14 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
+ * Registers a listener to be notified to start the keyguard exit animation.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener);
+
+ /**
* Reports that the password for the given user has changed.
*/
public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 673c6a5..8438118 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -405,6 +405,8 @@
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ static final int LOGTAG_INPUT_FOCUS = 62001;
+
/**
* Restrict ability of activities overriding transition animation in a way such that
* an activity can do it only when the transition happens within a same task.
@@ -413,7 +415,6 @@
*/
private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
"persist.wm.disable_custom_task_animation";
- static final int LOGTAG_INPUT_FOCUS = 62001;
/**
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -421,6 +422,19 @@
static boolean sDisableCustomTaskAnimationProperty =
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ public static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -770,7 +784,8 @@
final AnrController mAnrController;
private final ScreenshotHashController mScreenshotHashController;
- private final WindowContextListenerController mWindowContextListenerController =
+ @VisibleForTesting
+ final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@VisibleForTesting
@@ -2794,6 +2809,17 @@
return WindowManagerGlobal.ADD_OKAY;
}
+ /**
+ * Registers a listener for a {@link android.app.WindowContext} to subscribe to configuration
+ * changes of a {@link DisplayArea}.
+ *
+ * @param clientToken the window context's token
+ * @param type Window type of the window context
+ * @param displayId The display associated with the window context
+ * @param options A bundle used to pass window-related options and choose the right DisplayArea
+ *
+ * @return {@code true} if the listener was registered successfully.
+ */
@Override
public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
Bundle options) {
@@ -2849,6 +2875,7 @@
}
}
+ /** Returns {@code true} if this binder is a registered window token. */
@Override
public boolean isWindowToken(IBinder binder) {
synchronized (mGlobalLock) {
@@ -7750,6 +7777,15 @@
}
@Override
+ public void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ synchronized (mGlobalLock) {
+ getDefaultDisplayContentLocked().mAppTransition
+ .registerKeygaurdExitAnimationStartListener(listener);
+ }
+ }
+
+ @Override
public void reportPasswordChanged(int userId) {
mKeyguardDisableHandler.updateKeyguardEnabled(userId);
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8a512bc..cd18311 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.os.Process.INVALID_UID;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -26,7 +25,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -41,7 +39,6 @@
import android.annotation.CallSuper;
import android.annotation.Nullable;
-import android.app.IWindowToken;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -58,7 +55,6 @@
import android.view.SurfaceControl;
import android.view.WindowManager;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -112,17 +108,10 @@
private FixedRotationTransformState mFixedRotationTransformState;
- private Configuration mLastReportedConfig;
- private int mLastReportedDisplay = INVALID_DISPLAY;
-
/**
* When set to {@code true}, this window token is created from {@link android.app.WindowContext}
*/
- @VisibleForTesting
- final boolean mFromClientToken;
-
- private DeathRecipient mDeathRecipient;
- private boolean mBinderDied = false;
+ private final boolean mFromClientToken;
private final int mOwnerUid;
@@ -188,30 +177,6 @@
}
}
- private class DeathRecipient implements IBinder.DeathRecipient {
- private boolean mHasUnlinkToDeath = false;
-
- @Override
- public void binderDied() {
- synchronized (mWmService.mGlobalLock) {
- mBinderDied = true;
- removeImmediately();
- }
- }
-
- void linkToDeath() throws RemoteException {
- token.linkToDeath(DeathRecipient.this, 0);
- }
-
- void unlinkToDeath() {
- if (mHasUnlinkToDeath) {
- return;
- }
- token.unlinkToDeath(DeathRecipient.this, 0);
- mHasUnlinkToDeath = true;
- }
- }
-
/**
* Compares two child window of this token and returns -1 if the first is lesser than the
* second in terms of z-order and 1 otherwise.
@@ -266,17 +231,6 @@
if (dc != null) {
dc.addWindowToken(token, this);
}
- if (shouldReportToClient()) {
- try {
- mDeathRecipient = new DeathRecipient();
- mDeathRecipient.linkToDeath();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
- + "display " + dc.getDisplayId(), e);
- mDeathRecipient = null;
- return;
- }
- }
}
void removeAllWindowsIfPossible() {
@@ -414,22 +368,6 @@
// Needs to occur after the token is removed from the display above to avoid attempt at
// duplicate removal of this window container from it's parent.
super.removeImmediately();
-
- reportWindowTokenRemovedToClient();
- }
-
- // TODO(b/159767464): Remove after we migrate to listener approach.
- private void reportWindowTokenRemovedToClient() {
- if (!shouldReportToClient()) {
- return;
- }
- mDeathRecipient.unlinkToDeath();
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- try {
- windowTokenClient.onWindowTokenRemoved();
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
- }
}
@Override
@@ -441,51 +379,11 @@
// to another display before the window behind
// it is ready.
super.onDisplayChanged(dc);
- reportConfigToWindowTokenClient();
}
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
- reportConfigToWindowTokenClient();
- }
-
- void reportConfigToWindowTokenClient() {
- if (!shouldReportToClient()) {
- return;
- }
- if (mLastReportedConfig == null) {
- mLastReportedConfig = new Configuration();
- }
- final Configuration config = getConfiguration();
- final int displayId = getDisplayContent().getDisplayId();
- if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) {
- // No changes since last reported time.
- return;
- }
-
- mLastReportedConfig.setTo(config);
- mLastReportedDisplay = displayId;
-
- IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
- try {
- windowTokenClient.onConfigurationChanged(config, displayId);
- } catch (RemoteException e) {
- ProtoLog.w(WM_ERROR,
- "Could not report config changes to the window token client.");
- }
- }
-
- /**
- * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
- * registered from client side.
- */
- private boolean shouldReportToClient() {
- // Only report to client for WindowToken because Activities are updated through ATM
- // callbacks.
- return asActivityRecord() == null
- // Report to {@link android.view.WindowTokenClient} if this token was registered from it.
- && mFromClientToken && !mBinderDied;
}
@Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 11e4db5..160033e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.IDevicePolicyManager;
@@ -127,4 +128,7 @@
public void provisionFullyManagedDevice(
FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
}
+
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3d2e5de..717e77b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -910,12 +910,20 @@
};
protected static class RestrictionsListener implements UserRestrictionsListener {
- private Context mContext;
+ private final Context mContext;
+ private final UserManagerInternal mUserManagerInternal;
+ private final DevicePolicyManagerService mDpms;
- public RestrictionsListener(Context context) {
+ public RestrictionsListener(
+ Context context,
+ UserManagerInternal userManagerInternal,
+ DevicePolicyManagerService dpms) {
mContext = context;
+ mUserManagerInternal = userManagerInternal;
+ mDpms = dpms;
}
+ @Override
public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
Bundle prevRestrictions) {
final boolean newlyDisallowed =
@@ -925,13 +933,19 @@
final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
if (restrictionChanged) {
- // Notify ManagedProvisioning to update the built-in cross profile intent filters.
- Intent intent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- intent.setPackage(getManagedProvisioningPackage(mContext));
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ final int parentId = mUserManagerInternal.getProfileParentId(userId);
+ if (parentId == userId) {
+ return;
+ }
+
+ // Always reset filters on the parent user, which handles cross profile intent
+ // filters between the parent and its profiles.
+ Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+ + "change");
+ mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+ mContext.sendBroadcastAsUser(new Intent(
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+ UserHandle.of(userId));
}
}
}
@@ -1621,7 +1635,8 @@
mSetupContentObserver = new SetupContentObserver(mHandler);
- mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+ mUserManagerInternal.addUserRestrictionsListener(
+ new RestrictionsListener(mContext, mUserManagerInternal, this));
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
@@ -16008,19 +16023,9 @@
provisioningParams.isKeepAccountMigrated(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
- markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
- restrictRemovalOfManagedProfile(admin, userInfo.id);
+ setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
}
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
return userInfo.getUserHandle();
} catch (Exception e) {
DevicePolicyEventLogger
@@ -16250,21 +16255,20 @@
}
}
- private void markIsProfileOwnerOnOrganizationOwnedDevice(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+ private void setProfileOwnerOnOrgOwnedDeviceState(
+ ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+ synchronized (getLockObject()) {
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+ }
+ restrictRemovalOfManagedProfile(parentUserId);
}
- private void restrictRemovalOfManagedProfile(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).addUserRestriction(
- admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
- }
-
- private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
- final Context profileContext = mContext.createContextAsUser(
- UserHandle.of(profileId), /* flags= */ 0);
- return profileContext.getSystemService(DevicePolicyManager.class);
+ private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+ final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ /* value= */ true,
+ parentUserHandle);
}
@Override
@@ -16313,15 +16317,6 @@
}
disallowAddUser();
-
- final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
- .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
} catch (Exception e) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
@@ -16420,4 +16415,45 @@
.setStrings(callerPackage)
.write();
}
+
+ @Override
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ final int numOfProfiles = profiles.size();
+ if (numOfProfiles <= 1) {
+ return;
+ }
+
+ final String managedProvisioningPackageName = getManagedProvisioningPackage(
+ mContext);
+ // Removes cross profile intent filters from the parent to all the profiles.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, mContext.getOpPackageName());
+ // Setting and resetting default cross profile intent filters was previously handled
+ // by Managed Provisioning. For backwards compatibility, clear any intent filters
+ // that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, managedProvisioningPackageName);
+
+ // For each profile reset cross profile intent filters
+ for (int i = 0; i < numOfProfiles; i++) {
+ UserInfo profile = profiles.get(i);
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, mContext.getOpPackageName());
+ // Clear any intent filters that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, managedProvisioningPackageName);
+
+ mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853cc..7597cbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@
public void testDisallowSharingIntoProfileSetRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileClearRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileUnchanged() {
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
}
- private void verifyDataSharingChangedBroadcast() {
+ private void verifyDataSharingAppliedBroadcast() {
Intent expectedIntent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- expectedIntent.setPackage("com.android.managedprovisioning");
- expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntent(expectedIntent),
- MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index df19aeb..58ba907 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@
}
/**
- * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
* conditions.
*/
@Test
- public void testIsUidNetworkingBlocked() {
+ public void testCheckUidNetworkingBlocked() {
final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
// Metered network. Data saver on.
@@ -1877,17 +1877,16 @@
private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
- final NetworkPolicyManagerInternal npmi = LocalServices
- .getService(NetworkPolicyManagerInternal.class);
for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
final boolean expectedResult = pair.first;
final int rule = pair.second;
assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
- expectedResult,
- npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+ metered, backgroundRestricted));
assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
- npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+ backgroundRestricted));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 0000000..764c504
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+ @Test
+ public void testIsEmpty() {
+ assertThat(BundleUtils.isEmpty(null)).isTrue();
+ assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+ assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+ }
+
+ @Test
+ public void testClone() {
+ Bundle in = new Bundle();
+ Bundle out = BundleUtils.clone(in);
+ assertThat(in).isNotSameInstanceAs(out);
+ assertRestrictions(out, new Bundle());
+
+ out = BundleUtils.clone(null);
+ assertThat(out).isNotNull();
+ out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68..cd98d44 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@
@Test
public void testUserTypeBuilder_createUserType() {
final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+ final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+ final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+ final List<DefaultCrossProfileIntentFilter> filters = List.of(
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */false).build());
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(true)
.setMaxAllowed(21)
- .setBaseType(FLAG_FULL)
+ .setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
.setBadgeLabels(23, 24, 25)
.setBadgeColors(26, 27)
@@ -86,20 +93,45 @@
.setLabel(31)
.setMaxAllowedPerParent(32)
.setDefaultRestrictions(restrictions)
+ .setDefaultSystemSettings(systemSettings)
+ .setDefaultSecureSettings(secureSettings)
+ .setDefaultCrossProfileIntentFilters(filters)
.createUserTypeDetails();
assertEquals("a.name", type.getName());
assertTrue(type.isEnabled());
assertEquals(21, type.getMaxAllowed());
- assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+ assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
assertEquals(28, type.getIconBadge());
assertEquals(29, type.getBadgePlain());
assertEquals(30, type.getBadgeNoBackground());
assertEquals(31, type.getLabel());
assertEquals(32, type.getMaxAllowedPerParent());
+
assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
assertNotSame(restrictions, type.getDefaultRestrictions());
+ assertNotSame(systemSettings, type.getDefaultSystemSettings());
+ assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+ for (String key : systemSettings.keySet()) {
+ assertEquals(
+ systemSettings.getString(key),
+ type.getDefaultSystemSettings().getString(key));
+ }
+
+ assertNotSame(secureSettings, type.getDefaultSecureSettings());
+ assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+ for (String key : secureSettings.keySet()) {
+ assertEquals(
+ secureSettings.getString(key),
+ type.getDefaultSecureSettings().getString(key));
+ }
+
+ assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+ assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+ for (int i = 0; i < filters.size(); i++) {
+ assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+ }
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@
assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
assertEquals(Resources.ID_NULL, type.getLabel());
assertTrue(type.getDefaultRestrictions().isEmpty());
+ assertTrue(type.getDefaultSystemSettings().isEmpty());
+ assertTrue(type.getDefaultSecureSettings().isEmpty());
+ assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
assertFalse(type.hasBadge());
}
@@ -416,4 +451,13 @@
}
return bundle;
}
+
+ /** Creates a Bundle of the given settings keys and puts true for the value. */
+ private static Bundle makeSettingsBundle(String ... settings) {
+ final Bundle bundle = new Bundle();
+ for (String setting : settings) {
+ bundle.putBoolean(setting, true);
+ }
+ return bundle;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a9..ddf0cd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@
assertSame(in, UserRestrictionsUtils.nonNull(in));
}
- public void testIsEmpty() {
- assertTrue(UserRestrictionsUtils.isEmpty(null));
- assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
- assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
- }
-
- public void testClone() {
- Bundle in = new Bundle();
- Bundle out = UserRestrictionsUtils.clone(in);
- assertNotSame(in, out);
- assertRestrictions(out, new Bundle());
-
- out = UserRestrictionsUtils.clone(null);
- assertNotNull(out);
- out.putBoolean("a", true); // Should not be Bundle.EMPTY.
- }
-
public void testMerge() {
Bundle a = newRestrictions("a", "d");
Bundle b = newRestrictions("b", "d", "e");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index fc1cb70..acda4d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -29,8 +29,8 @@
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 57d5323..72c6028 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,8 +33,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 42b080e..9385110 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@
new RemoteAnimationAdapter(new Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449..71f1914 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -70,8 +71,10 @@
class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
for (RemoteAnimationTarget target : apps) {
assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f1e3609..83aca5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f75c98f..78074d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -26,7 +27,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -70,13 +70,15 @@
spyOn(wms);
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
- final IBinder token = (IBinder) args[0];
- final int windowType = (int) args[1];
- new WindowToken(mWm, token, windowType, true /* persistOnEmpty */,
- mDefaultDisplay, true /* ownerCanManageAppTokens */, 1000 /* ownerUid */,
- false /* roundedCornerOverlay */, true /* fromClientToken */);
- return WindowManagerGlobal.ADD_OKAY;
- }).when(wms).addWindowTokenWithOptions(any(), anyInt(), anyInt(), any(), anyString());
+ IBinder clientToken = (IBinder) args[0];
+ int displayId = (int) args[2];
+ DisplayContent dc = mWm.mRoot.getDisplayContent(displayId);
+ mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
+ dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
+ null /* options */);
+ return true;
+ }).when(wms).registerWindowContextListener(any(), eq(TYPE_INPUT_METHOD_DIALOG),
+ anyInt(), any());
mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build();
@@ -95,14 +97,12 @@
assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
- // Obtain the context again and check they are the same instance and match the display
- // metrics of the secondary display.
+ // Obtain the context again and check if the window metrics match the IME container bounds
+ // of the secondary display.
final Context contextOnSecondaryDisplay = mController.getSettingsContext(
mSecondaryDisplay.getDisplayId());
assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
- assertThat(contextOnDefaultDisplay.getWindowContextToken())
- .isEqualTo(contextOnSecondaryDisplay.getWindowContextToken());
}
private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3ae..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@
mColor = Color.GREEN;
assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2efd4b5..15e045c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
adapter.onAnimationCancelled(mMockLeash);
verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -170,7 +176,7 @@
null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -190,7 +196,7 @@
@Test
public void testZeroAnimations() {
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_NONE);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -199,7 +205,7 @@
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -213,15 +219,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
win.mActivityRecord.removeImmediately();
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
@@ -255,15 +264,18 @@
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
} finally {
@@ -383,15 +401,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6f775cf..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -119,13 +117,13 @@
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // Create landscape freeform display and a freeform app.
+ // create freeform display and a freeform app
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into portrait freeform and then make it a compat app.
+ // Put app window into freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@
final int density = mActivity.getConfiguration().densityDpi;
- // Change display configuration to fullscreen.
+ // change display configuration to fullscreen
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
- // Size compat mode is sandboxed at the activity level.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -175,12 +171,6 @@
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -190,17 +180,9 @@
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
}
@Test
@@ -228,9 +210,6 @@
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
- // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
- // max aspect ratio.
- assertActivityMaxBoundsSandboxedForSizeCompat();
assertScaled();
}
@@ -238,13 +217,11 @@
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
- // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
- // Activity is sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -309,8 +280,6 @@
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -349,13 +316,14 @@
}
@Test
- public void testMoveToDifferentOrientationDisplay() {
+ public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+ final int origWidth = configBounds.width();
+ final int origHeight = configBounds.height();
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(originalBounds.width(), currentBounds.width());
- assertEquals(originalBounds.height(), currentBounds.height());
+ assertEquals(origWidth, configBounds.width());
+ assertEquals(origHeight, configBounds.height());
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode on the new display.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * originalBounds.height()
- / originalBounds.width()));
+ (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), currentBounds.height());
- assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- currentBounds.width());
+ assertEquals(newDisplayBounds.height(), configBounds.height());
+ assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ configBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(currentBounds.width(), appBounds.width());
- assertEquals(currentBounds.height() - notchHeight, appBounds.height());
- // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(configBounds.width(), appBounds.width());
+ assertEquals(configBounds.height() - notchHeight, appBounds.height());
}
@Test
- public void testFixedOrientationRotateCutoutDisplay() {
+ public void testFixedOrientRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- final int width = 1000;
- setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+ setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
- final float maxAspect = 1.4f;
+ // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
- // Activity is sandboxed, and bounds include the area consumed by the notch.
- assertActivityMaxBoundsSandboxedForLetterbox();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
-
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
- public void testFixedAspectRatioOrientationChangeOrientation() {
+ public void testFixedAspOrientChangeOrient() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -510,8 +459,6 @@
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -659,9 +602,6 @@
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- // Activity is sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
-
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -696,8 +635,6 @@
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertTaskMaxBoundsSandboxed();
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -741,30 +676,29 @@
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+ Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ Rect activityBounds = new Rect(mActivity.getBounds());
+
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Activity and task inherit max bounds from TaskDisplayArea.
- assertMaxBoundsInheritDisplayAreaBounds();
+ assertEquals(displayBounds, activityBounds);
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
- assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+ displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ activityBounds = new Rect(mActivity.getBounds());
+ assertTrue(displayBounds.width() > displayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
- assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
- / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ activityBounds.width());
}
@Test
@@ -797,17 +731,14 @@
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
- final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
- assertThat(taskBounds.width())
- .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
}
@Test
@@ -847,14 +778,6 @@
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
- // New activity max bounds are sandboxed due to letterbox.
- assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskBounds);
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(displayBounds.height());
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
- .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
// App bounds should be fullscreen in Task bounds.
assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed due to size compat.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -913,7 +831,6 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -947,26 +862,20 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed due to mismatched orientation request.
- assertTaskMaxBoundsSandboxed();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- // Activity max bounds are sandboxed due to unresizable app.
- assertActivityMaxBoundsSandboxedForSizeCompat();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed, as in first case.
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -983,18 +892,12 @@
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
- taskDisplayArea.setBounds(displayAreaBounds);
+ taskDisplayArea.setBounds(0, 0, 2400, 1000);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
- // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
}
@Test
@@ -1142,48 +1045,6 @@
assertFalse(mActivity.hasSizeCompatBounds());
}
- /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
- private void assertMaxBoundsInheritDisplayAreaBounds() {
- final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- }
-
- /**
- * Asserts task-level letterboxing, so both activity and task max bounds
- * are sandboxed to the letterbox bounds.
- */
- private void assertTaskMaxBoundsSandboxed() {
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- // Task max bounds are sandboxed due to letterbox.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- }
-
- /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForSizeCompat() {
- // Activity max bounds are sandboxed due to size compat mode.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getWindowConfiguration().getBounds());
- // Task inherits max bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
- /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForLetterbox() {
- // Activity is sandboxed due to fixed aspect ratio.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getBounds());
- // Task inherits bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513d..0eb8c8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@
}
@Test
+ public void testNotSaveLaunchingStateForNonLeafTask() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ final Task leafTask = createTaskInStack(task, 0 /* userId */);
+
+ leafTask.setHasBeenVisible(true);
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ verify(persister, never()).saveTask(same(task), any());
+ verify(persister).saveTask(same(leafTask), any());
+ }
+
+ @Test
public void testNotSpecifyOrientationByFloatingTask() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true).setCreateParentTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993d..ae85ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
- // Ensure letterbox aspect ratio is not overridden on any device target.
- // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
- // the below method, is set on some device form factors.
- mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index df5b48a..99c96bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -905,8 +906,10 @@
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
try {
finishedCallback.onAnimationFinished();
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 7cc233b..cac7b82 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -84,7 +84,6 @@
static_libs: [
"libviewcompiler",
],
- test_suites: ["general-tests"],
}
cc_binary_host {
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5f7d3f9..791e471 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -10,10 +10,6 @@
"include-filter": "android.view.cts.PrecompiledLayoutTest"
}
]
- },
- {
- "name": "view-compiler-tests",
- "host": true
}
]
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7215cd5..ac584c1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4038,6 +4038,20 @@
public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
KEY_PREFIX + "enable_presence_group_subscribe_bool";
+ /**
+ * An integer key associated with the period of time in seconds the non-rcs capability
+ * information of each contact is cached on the device.
+ * <p>
+ * The rcs capability cache expiration sec is managed by
+ * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+ * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+ * config.
+ * <p>
+ * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+ */
+ public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+ KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -4048,6 +4062,7 @@
defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
+ defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1..1473b7a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -150,6 +151,22 @@
public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
"cache_key.telephony.get_slot_index";
+ /** @hide */
+ public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+ /** @hide */
+ public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+ "restoreSimSpecificSettings";
+
+ /**
+ * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+ * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+ * #restoreAllSimSpecificSettings()}.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
private static final int MAX_CACHE_SIZE = 4;
private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@
public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
CONTENT_URI, "wfc_roaming_enabled");
+
+ /**
+ * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+ * settings
+ * <p>
+ * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+ * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ CONTENT_URI, "backup_and_restore");
+
+ /**
+ * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+ * SuW.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
/**
* A content {@link Uri} used to receive updates on cross sim enabled user setting.
* <p>
@@ -3455,4 +3494,71 @@
sSlotIndexCache.clear();
sPhoneIdCache.clear();
}
+
+ /**
+ * Called to retrieve SIM-specific settings data to be backed up.
+ *
+ * @return data in byte[] to be backed up.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public byte[] getAllSimSpecificSettingsForBackup() {
+ Bundle bundle = mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+ return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+ }
+
+ /**
+ * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+ * This will try to restore the data that was stored internally when {@link
+ * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+ * End result is SimInfoDB is modified to match any backed up configs for the requested
+ * inserted sim.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param iccId of the sim that a restore is requested for.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ iccId, null);
+ }
+
+ /**
+ * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+ * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+ * data in an internal file. This file will persist on device for device's lifetime and will be
+ * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+ * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+ * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param data with the sim specific configs to be backed up.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ null, bundle);
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 2e35d27..5f8e93d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -44,8 +44,12 @@
@Override
public void setListener(IImsEcbmListener listener) {
synchronized (mLock) {
- if (mImsEcbm != null && listener != null && Objects.equals(
- mImsEcbm.asBinder(), listener.asBinder())) {
+ if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mListener = null;
+ }
+ if (mListener != null && listener != null && Objects.equals(
+ mListener.asBinder(), listener.asBinder())) {
return;
}
if (listener == null) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 555a47e..8e961ac 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -48,6 +48,10 @@
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
synchronized (mLock) {
+ if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mListener = null;
+ }
if (mListener != null && listener != null && Objects.equals(
mListener.asBinder(), listener.asBinder())) {
return;
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eef4fca..83b89aa 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -23,6 +23,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.telephony.ims.ImsUtListener;
+import android.util.Log;
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
@@ -41,6 +42,7 @@
// will break other implementations of ImsUt maintained by other ImsServices.
@SystemApi
public class ImsUtImplBase {
+ private static final String TAG = "ImsUtImplBase";
/**
* Bar all incoming calls. (See 3GPP TS 24.611)
* @hide
@@ -207,6 +209,11 @@
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
synchronized (mLock) {
+ if (mUtListener != null
+ && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+ Log.w(TAG, "setListener: discarding dead Binder");
+ mUtListener = null;
+ }
if (mUtListener != null && listener != null && Objects.equals(
mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
return;
diff --git a/tests/UpdatableSystemFontTest/TEST_MAPPING b/tests/UpdatableSystemFontTest/TEST_MAPPING
new file mode 100644
index 0000000..a5c4479
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "UpdatableSystemFontTest"
+ }
+ ]
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c7554f6..0674138 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1262,22 +1262,28 @@
}
}
- private void updateUidNetworkingBlocked() {
- doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
- i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
- mRestrictBackground)
+ private void mockUidNetworkingBlocked() {
+ doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+ i.getArgument(1) /* metered */, mRestrictBackground)
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+ doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+ .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+ inv.getArgument(1) /* uidRules */,
+ inv.getArgument(2) /* isNetworkMetered */,
+ inv.getArgument(3) /* isBackgroundRestricted */)
+ ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+ anyInt(), anyInt(), anyBoolean(), anyBoolean());
}
private void setUidRulesChanged(int uidRules) throws RemoteException {
mUidRules = uidRules;
- updateUidNetworkingBlocked();
mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
}
private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
mRestrictBackground = restrictBackground;
- updateUidNetworkingBlocked();
mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
}
@@ -6809,6 +6815,7 @@
.addTransportType(TRANSPORT_CELLULAR)
.build();
mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ mockUidNetworkingBlocked();
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
@@ -6891,6 +6898,7 @@
public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
+ mockUidNetworkingBlocked();
// No Networkcallbacks invoked before any network is active.
setUidRulesChanged(RULE_REJECT_ALL);
@@ -7160,6 +7168,13 @@
when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
}
+ private void establishLegacyLockdownVpn() throws Exception {
+ // The legacy lockdown VPN only supports userId 0.
+ final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+ mMockVpn.registerAgent(ranges);
+ mMockVpn.connect(true);
+ }
+
@Test
public void testLegacyLockdownVpn() throws Exception {
mServiceContext.setPermission(
@@ -7254,22 +7269,30 @@
mMockVpn.expectStartLegacyVpnRunner();
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
b1.expectBroadcast();
b2.expectBroadcast();
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wlan0");
wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ final NetworkCapabilities wifiNc = new NetworkCapabilities();
+ wifiNc.addTransportType(TRANSPORT_WIFI);
+ wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
// Wifi is CONNECTING because the VPN isn't up yet.
@@ -7302,16 +7325,20 @@
// The VPN comes up again on wifi.
b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
- mMockVpn.establishForMyUid();
+ establishLegacyLockdownVpn();
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
b1.expectBroadcast();
b2.expectBroadcast();
-
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+ vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+ assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+ assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect cell. Nothing much happens since it's not the default network.
// Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f478282..32c6a75 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -213,6 +215,8 @@
when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemServiceName(NotificationManager.class))
@@ -253,12 +257,14 @@
@Test
public void testRestrictedProfilesAreAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
final Vpn vpn = createVpn(primaryUser.id);
- final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
- null, null);
+
+ // Assume the user can have restricted profiles.
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ final Set<UidRange> ranges =
+ vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -267,7 +273,6 @@
@Test
public void testManagedProfilesAreNotAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, managedProfileA);
final Vpn vpn = createVpn(primaryUser.id);
@@ -290,7 +295,6 @@
@Test
public void testUidAllowAndDenylist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -316,7 +320,6 @@
@Test
public void testGetAlwaysAndOnGetLockDown() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
// Default state.
@@ -341,7 +344,6 @@
@Test
public void testLockdownChangingPackage() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -369,7 +371,6 @@
@Test
public void testLockdownAllowlist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -444,7 +445,6 @@
@Test
public void testLockdownRuleRepeatability() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -477,7 +477,6 @@
@Test
public void testLockdownRuleReversibility() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] entireUser = {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -954,7 +953,14 @@
}
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
- setMockedUsers(primaryUser);
+ // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
+ // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
+ // setMockedUsers(primaryUser);
+ final ArrayList<UserInfo> users = new ArrayList<>();
+ users.add(primaryUser);
+ when(mUserManager.getAliveUsers()).thenReturn(users);
+ when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
+ when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
// Dummy egress interface
final LinkProperties lp = new LinkProperties();
@@ -997,14 +1003,12 @@
profile.ipsecIdentifier = "id";
profile.ipsecSecret = "secret";
profile.l2tpSecret = "l2tpsecret";
+
when(mConnectivityManager.getAllNetworks())
.thenReturn(new Network[] { new Network(101) });
+
when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
- anyInt(), any(), anyInt())).thenAnswer(invocation -> {
- // The runner has registered an agent and is now ready.
- legacyRunnerReady.open();
- return new Network(102);
- });
+ anyInt(), any(), anyInt())).thenReturn(new Network(102));
final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
@@ -1020,14 +1024,20 @@
"linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
"idle", "1800", "mtu", "1270", "mru", "1270" },
deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
// Now wait for the runner to be ready before testing for the route.
- legacyRunnerReady.block(10_000);
- // In this test the expected address is always v4 so /32
+ ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+ verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+ lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+ // In this test the expected address is always v4 so /32.
+ // Note that the interface needs to be specified because RouteInfo objects stored in
+ // LinkProperties objects always acquire the LinkProperties' interface.
final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
- RouteInfo.RTN_THROW);
- assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
- + vpn.mConfig.routes,
- vpn.mConfig.routes.contains(expectedRoute));
+ null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+ final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+ assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+ actualRoutes.contains(expectedRoute));
} finally {
// Now interrupt the thread, unblock the runner and clean up.
vpn.mVpnRunner.exitVpnRunner();
@@ -1083,6 +1093,11 @@
}
@Override
+ public PendingIntent getIntentForStatusPanel(Context context) {
+ return null;
+ }
+
+ @Override
public void sendArgumentsToDaemon(
final String daemon, final LocalSocket socket, final String[] arguments,
final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1144,6 +1159,10 @@
doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
.thenReturn(asUserContext);
+ when(asUserContext.getSystemServiceName(UserManager.class))
+ .thenReturn(Context.USER_SERVICE);
+ when(asUserContext.getSystemService(UserManager.class))
+ .thenReturn(mUserManager);
final TestLooper testLooper = new TestLooper();
final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
@@ -1179,11 +1198,6 @@
final int id = (int) invocation.getArguments()[0];
return userMap.get(id);
}).when(mUserManager).getUserInfo(anyInt());
-
- doAnswer(invocation -> {
- final int id = (int) invocation.getArguments()[0];
- return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
- }).when(mUserManager).canHaveRestrictedProfile();
}
/**
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a7d0860..a07bce3 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,8 @@
"android.view.InsetsSourceTest",
"android.view.InsetsSourceConsumerTest",
"android.view.InsetsStateTest",
+ "android.view.RoundedCornerTest",
+ "android.view.RoundedCornersTest",
"android.view.WindowMetricsTest",
"android.view.PendingInsetsControllerTest",
"android.app.WindowContextTest",
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
new VcnGatewayConnectionConfig.Builder()
.setRetryInterval(RETRY_INTERVALS_MS)
.setMaxMtu(MAX_MTU);
- for (int caps : EXPOSED_CAPS) {
+ for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e32e1e8..4859644 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -142,6 +143,9 @@
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
+ private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+ ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +188,7 @@
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class);
- }).when(mMockDeps).newVcn(any(), any(), any(), any());
+ }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
@@ -307,7 +311,7 @@
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps)
- .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot));
+ .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@Test
@@ -485,7 +489,8 @@
eq(mVcnContext),
eq(TEST_UUID_2),
eq(TEST_VCN_CONFIG),
- eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT));
+ eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+ any());
// Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -634,4 +639,25 @@
verify(mMockPolicyListener).onPolicyChanged();
}
+
+ @Test
+ public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_1),
+ eq(TEST_VCN_CONFIG),
+ eq(snapshot),
+ mSafemodeCallbackCaptor.capture());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+ safemodeCallback.onEnteredSafemode();
+
+ assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index fbaae6f..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -45,7 +45,12 @@
public void testEnterWhileNotRunningTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
new VcnGatewayConnection(
- mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
vgc.setIsRunning(false);
vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index df1341c..333b5b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -43,6 +43,7 @@
import com.android.server.IpSecService;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@@ -80,6 +81,7 @@
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@@ -94,6 +96,7 @@
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
@@ -123,7 +126,12 @@
mGatewayConnection =
new VcnGatewayConnection(
- mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
}
protected IpSecTransform makeDummyIpSecTransform() throws Exception {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 0c1df76..66cbf84 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,22 +16,27 @@
package com.android.server.vcn;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
@@ -51,9 +56,13 @@
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
+ private VcnSafemodeCallback mVcnSafemodeCallback;
private Vcn.Dependencies mDeps;
+ private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
private TestLooper mTestLooper;
+ private VcnGatewayConnectionConfig mGatewayConnectionConfig;
private VcnConfig mConfig;
private Vcn mVcn;
@@ -63,6 +72,7 @@
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
@@ -76,15 +86,26 @@
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(VcnGatewayConnection.class);
- }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any());
+ }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
- mConfig =
- new VcnConfig.Builder(mContext)
- .addGatewayConnectionConfig(
- VcnGatewayConnectionConfigTest.buildTestConfig())
- .build();
+ mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
- mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps);
+ final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ configBuilder.addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ }
+ configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+ mConfig = configBuilder.build();
+
+ mVcn =
+ new Vcn(
+ mVcnContext,
+ TEST_SUB_GROUP,
+ mConfig,
+ mSubscriptionSnapshot,
+ mVcnSafemodeCallback,
+ mDeps);
}
private NetworkRequestListener verifyAndGetRequestListener() {
@@ -95,23 +116,22 @@
return mNetworkRequestListenerCaptor.getValue();
}
- private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) {
- final NetworkRequest.Builder builder = new NetworkRequest.Builder();
- for (final int netCapability : networkCapabilities) {
- builder.addCapability(netCapability);
+ private void startVcnGatewayWithCapabilities(
+ NetworkRequestListener requestListener, int... netCapabilities) {
+ final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ for (final int netCapability : netCapabilities) {
+ requestBuilder.addCapability(netCapability);
}
- return builder.build();
+
+ requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+ mTestLooper.dispatchAll();
}
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-
- requestListener.onNetworkRequested(
- getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS),
- NETWORK_SCORE,
- PROVIDER_ID);
- mTestLooper.dispatchAll();
+ startVcnGatewayWithCapabilities(
+ requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
@@ -126,4 +146,38 @@
verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, capability);
+ }
+
+ // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+ // Expect one VcnGatewayConnection per capability.
+ final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertEquals(numExpectedGateways, gatewayConnections.size());
+ verify(mDeps, times(numExpectedGateways))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+ // all Gateways
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onEnteredSafemode();
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+ verify(gatewayConnection).teardownAsynchronously();
+ }
+ verify(mVcnNetworkProvider).unregisterListener(requestListener);
+ verify(mVcnSafemodeCallback).onEnteredSafemode();
+ }
}