Merge "Log when latency tracker is enabled or disabled" into main
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index b56dceb..2cbd665 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -723,7 +723,7 @@
bool BootAnimation::preloadAnimation() {
ATRACE_CALL();
findBootAnimationFile();
- if (!mZipFileName.isEmpty()) {
+ if (!mZipFileName.empty()) {
mAnimation = loadAnimation(mZipFileName);
return (mAnimation != nullptr);
}
@@ -842,7 +842,7 @@
// We have no bootanimation file, so we use the stock android logo
// animation.
- if (mZipFileName.isEmpty()) {
+ if (mZipFileName.empty()) {
ALOGD("No animation file");
result = android();
} else {
diff --git a/core/api/current.txt b/core/api/current.txt
index bcb21e5..d26cf2e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10560,6 +10560,7 @@
public final class ContextParams {
method @Nullable public String getAttributionTag();
method @Nullable public android.content.AttributionSource getNextAttributionSource();
+ method @NonNull public boolean shouldRegisterAttributionSource();
}
public static final class ContextParams.Builder {
@@ -10568,6 +10569,7 @@
method @NonNull public android.content.ContextParams build();
method @NonNull public android.content.ContextParams.Builder setAttributionTag(@Nullable String);
method @NonNull public android.content.ContextParams.Builder setNextAttributionSource(@Nullable android.content.AttributionSource);
+ method @NonNull public android.content.ContextParams.Builder setShouldRegisterAttributionSource(boolean);
}
public class ContextWrapper extends android.content.Context {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2137f47..4275167 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3793,6 +3793,10 @@
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
+ public class PackageArchiver {
+ method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ }
+
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
@@ -3890,6 +3894,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
+ method @NonNull public android.content.pm.PackageArchiver getPackageArchiver();
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public int getPackageUidAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cff5e40..d43d785 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -497,14 +497,14 @@
method public int describeContents();
method public int getActivityType();
method @Nullable public android.graphics.Rect getAppBounds();
- method @NonNull public android.graphics.Rect getBounds();
+ method public android.graphics.Rect getBounds();
method @NonNull public android.graphics.Rect getMaxBounds();
method public int getRotation();
method public int getWindowingMode();
method public static boolean isFloating(int);
method public void setActivityType(int);
method public void setAppBounds(@Nullable android.graphics.Rect);
- method public void setBounds(@Nullable android.graphics.Rect);
+ method public void setBounds(android.graphics.Rect);
method public void setMaxBounds(@Nullable android.graphics.Rect);
method public void setRotation(int);
method public void setTo(android.app.WindowConfiguration);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0255860..fcd13b8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -64,6 +64,7 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
+import android.content.pm.PackageArchiver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
@@ -172,6 +173,7 @@
private volatile UserManager mUserManager;
private volatile PermissionManager mPermissionManager;
private volatile PackageInstaller mInstaller;
+ private volatile PackageArchiver mPackageArchiver;
private volatile ArtManager mArtManager;
private volatile DevicePolicyManager mDevicePolicyManager;
private volatile String mPermissionsControllerPackageName;
@@ -3282,6 +3284,18 @@
}
@Override
+ public PackageArchiver getPackageArchiver() {
+ if (mPackageArchiver == null) {
+ try {
+ mPackageArchiver = new PackageArchiver(mContext, mPM.getPackageArchiverService());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return mPackageArchiver;
+ }
+
+ @Override
public boolean isPackageAvailable(String packageName) {
try {
return mPM.isPackageAvailable(packageName, getUserId());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5feafbe..a538247 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3455,20 +3455,20 @@
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
mParams = Objects.requireNonNull(params);
mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource,
- params.getRenouncedPermissions());
+ params.getRenouncedPermissions(), params.shouldRegisterAttributionSource());
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
private @NonNull AttributionSource createAttributionSource(@Nullable String attributionTag,
@Nullable AttributionSource nextAttributionSource,
- @Nullable Set<String> renouncedPermissions) {
+ @Nullable Set<String> renouncedPermissions, boolean shouldRegister) {
AttributionSource attributionSource = new AttributionSource(Process.myUid(),
Process.myPid(), mOpPackageName, attributionTag,
(renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null,
getDeviceId(), nextAttributionSource);
// If we want to access protected data on behalf of another app we need to
// tell the OS that we opt in to participate in the attribution chain.
- if (nextAttributionSource != null) {
+ if (nextAttributionSource != null || shouldRegister) {
attributionSource = getSystemService(PermissionManager.class)
.registerAttributionSource(attributionSource);
}
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 729e555..0857c96 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -40,7 +40,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -195,7 +195,8 @@
XmlUtils.beginDocument(parser, TAG_LOCALE_CONFIG);
int outerDepth = parser.getDepth();
AttributeSet attrs = Xml.asAttributeSet(parser);
- Set<String> localeNames = new HashSet<String>();
+ // LinkedHashSet to preserve insertion order
+ Set<String> localeNames = new LinkedHashSet<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_LOCALE.equals(parser.getName())) {
final TypedArray attributes = res.obtainAttributes(
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fbb97ff..cbbf4e0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -868,10 +868,6 @@
@Override
public VirtualDeviceManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- if (!ctx.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
- return null;
- }
if (!ctx.getResources().getBoolean(R.bool.config_enableVirtualDeviceManager)) {
return null;
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index e3055e5..bf238c3 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -267,11 +267,12 @@
}
};
+ // TODO(b/297672475): make this take @Nullable
/**
* Sets the bounds to the provided {@link Rect}.
* @param rect the new bounds value.
*/
- public void setBounds(@Nullable Rect rect) {
+ public void setBounds(Rect rect) {
if (rect == null) {
mBounds.setEmpty();
return;
@@ -363,8 +364,8 @@
return mAppBounds;
}
+ // TODO(b/297672475): make this return @NonNull
/** @see #setBounds(Rect) */
- @NonNull
public Rect getBounds() {
return mBounds;
}
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index 5cc3a24..988a9c0 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -50,17 +50,20 @@
private final @Nullable String mAttributionTag;
private final @Nullable AttributionSource mNext;
private final @NonNull Set<String> mRenouncedPermissions;
+ private final boolean mShouldRegisterAttributionSource;
/** {@hide} */
public static final ContextParams EMPTY = new ContextParams.Builder().build();
private ContextParams(@Nullable String attributionTag,
@Nullable AttributionSource next,
- @Nullable Set<String> renouncedPermissions) {
+ @Nullable Set<String> renouncedPermissions,
+ boolean shouldRegister) {
mAttributionTag = attributionTag;
mNext = next;
mRenouncedPermissions = (renouncedPermissions != null)
? renouncedPermissions : Collections.emptySet();
+ mShouldRegisterAttributionSource = shouldRegister;
}
/**
@@ -95,12 +98,22 @@
}
/**
+ * @return Whether the attribution source associated with the Context being created should be
+ * registered.
+ */
+ @NonNull
+ public boolean shouldRegisterAttributionSource() {
+ return mShouldRegisterAttributionSource;
+ }
+
+ /**
* Builder for creating a {@link ContextParams}.
*/
public static final class Builder {
private @Nullable String mAttributionTag;
private @NonNull Set<String> mRenouncedPermissions = Collections.emptySet();
private @Nullable AttributionSource mNext;
+ private boolean mShouldRegisterAttributionSource;
/**
* Create a new builder.
@@ -159,6 +172,19 @@
}
/**
+ * Sets whether the attribution source associated with the context created from these params
+ * should be registered.
+ *
+ * @param shouldRegister Whether the attribution source associated with the Context being
+ * created should be registered.
+ */
+ @NonNull
+ public Builder setShouldRegisterAttributionSource(boolean shouldRegister) {
+ mShouldRegisterAttributionSource = shouldRegister;
+ return this;
+ }
+
+ /**
* Sets permissions which have been voluntarily "renounced" by the
* calling app.
* <p>
@@ -205,7 +231,7 @@
@NonNull
public ContextParams build() {
return new ContextParams(mAttributionTag, mNext,
- mRenouncedPermissions);
+ mRenouncedPermissions, mShouldRegisterAttributionSource);
}
}
}
diff --git a/core/java/android/content/pm/IPackageArchiverService.aidl b/core/java/android/content/pm/IPackageArchiverService.aidl
new file mode 100644
index 0000000..fc471c4
--- /dev/null
+++ b/core/java/android/content/pm/IPackageArchiverService.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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.IntentSender;
+import android.os.UserHandle;
+
+/** {@hide} */
+interface IPackageArchiverService {
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
+ void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ea0f5ff..4ed4dd3 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -25,6 +25,7 @@
import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageArchiverService;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
import android.content.pm.IOnChecksumsReadyListener;
@@ -650,6 +651,8 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
IPackageInstaller getPackageInstaller();
+ IPackageArchiverService getPackageArchiverService();
+
@EnforcePermission("DELETE_PACKAGES")
boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
@UnsupportedAppUsage
diff --git a/core/java/android/content/pm/PackageArchiver.java b/core/java/android/content/pm/PackageArchiver.java
new file mode 100644
index 0000000..d739d50
--- /dev/null
+++ b/core/java/android/content/pm/PackageArchiver.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 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.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.ParcelableException;
+import android.os.RemoteException;
+
+/**
+ * {@code ArchiveManager} is used to archive apps. During the archival process, the apps APKs and
+ * cache are removed from the device while the user data is kept. Through the
+ * {@code requestUnarchive()} call, apps can be restored again through their responsible app store.
+ *
+ * <p> Archived apps are returned as displayable apps through the {@link LauncherApps} APIs and
+ * will be displayed to users with UI treatment to highlight that said apps are archived. If
+ * a user taps on an archived app, the app will be unarchived and the restoration process is
+ * communicated.
+ *
+ * @hide
+ */
+// TODO(b/278560219) Improve public documentation.
+@SystemApi
+public class PackageArchiver {
+
+ private final Context mContext;
+ private final IPackageArchiverService mService;
+
+ /**
+ * @hide
+ */
+ public PackageArchiver(Context context, IPackageArchiverService service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Requests to archive a package which is currently installed.
+ *
+ * @param statusReceiver Callback used to notify when the operation is completed.
+ * @throws NameNotFoundException If {@code packageName} isn't found or not available to the
+ * caller.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
+ @SystemApi
+ public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
+ throws NameNotFoundException {
+ try {
+ mService.requestArchive(packageName, mContext.getPackageName(), statusReceiver,
+ mContext.getUser());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(NameNotFoundException.class);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 885e67e1..d2173a6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9934,6 +9934,16 @@
public abstract @NonNull PackageInstaller getPackageInstaller();
/**
+ * {@link PackageArchiver} can be used to archive and restore archived packages.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull PackageArchiver getPackageArchiver() {
+ throw new UnsupportedOperationException(
+ "getPackageArchiver not implemented in subclass");
+ }
+ /**
* Adds a {@code CrossProfileIntentFilter}. After calling this method all
* intents sent from the user with id sourceUserId can also be be resolved
* by activities in the user with id targetUserId if they match the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9387ae1..41ba1dc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1456,8 +1456,8 @@
private static AssetManager newConfiguredAssetManager() {
AssetManager assetManager = new AssetManager();
- assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assetManager.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
return assetManager;
}
@@ -9011,8 +9011,8 @@
}
AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
mCachedAssetManager = assets;
@@ -9086,8 +9086,8 @@
private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
final AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
return assets;
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index b225de4..23b9d0b 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1480,9 +1480,13 @@
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
int majorVersion) {
- synchronized (this) {
- ensureValidLocked();
- nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+ if (locale != null) {
+ setConfiguration(mcc, mnc, null, new String[]{locale}, orientation, touchscreen,
+ density, keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+ smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+ colorMode, grammaticalGender, majorVersion);
+ } else {
+ setConfiguration(mcc, mnc, null, null, orientation, touchscreen, density,
keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
colorMode, grammaticalGender, majorVersion);
@@ -1490,6 +1494,25 @@
}
/**
+ * Change the configuration used when retrieving resources. Not for use by
+ * applications.
+ * @hide
+ */
+ public void setConfiguration(int mcc, int mnc, String defaultLocale, String[] locales,
+ int orientation, int touchscreen, int density, int keyboard, int keyboardHidden,
+ int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
+ int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
+ int grammaticalGender, int majorVersion) {
+ synchronized (this) {
+ ensureValidLocked();
+ nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation,
+ touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
+ screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
+ screenLayout, uiMode, colorMode, grammaticalGender, majorVersion);
+ }
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -1572,10 +1595,11 @@
private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
boolean invalidateCaches);
private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
- @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
- int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
- int uiMode, int colorMode, int grammaticalGender, int majorVersion);
+ @Nullable String defaultLocale, @NonNull String[] locales, int orientation,
+ int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+ int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+ int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
+ int majorVersion);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
long ptr, boolean includeOverlays, boolean includeLoaders);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 395fef2..76b29e6 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -425,14 +425,12 @@
mConfiguration.setLocales(locales);
}
+ String[] selectedLocales = null;
+ String defaultLocale = null;
if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
if (locales.size() > 1) {
String[] availableLocales;
-
- LocaleList localeList = ResourcesManager.getInstance().getLocaleList();
- if (!localeList.isEmpty()) {
- availableLocales = localeList.toLanguageTags().split(",");
- } else {
+ if (ResourcesManager.getInstance().getLocaleList().isEmpty()) {
// The LocaleList has changed. We must query the AssetManager's
// available Locales and figure out the best matching Locale in the new
// LocaleList.
@@ -444,13 +442,35 @@
availableLocales = null;
}
}
- }
- if (availableLocales != null) {
- final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
- availableLocales);
- if (bestLocale != null && bestLocale != locales.get(0)) {
- mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+
+ if (availableLocales != null) {
+ final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+ availableLocales);
+ if (bestLocale != null) {
+ selectedLocales = new String[]{
+ adjustLanguageTag(bestLocale.toLanguageTag())};
+ if (!bestLocale.equals(locales.get(0))) {
+ mConfiguration.setLocales(
+ new LocaleList(bestLocale, locales));
+ }
+ }
}
+ } else {
+ selectedLocales = locales.getIntersection(
+ ResourcesManager.getInstance().getLocaleList());
+ defaultLocale = ResourcesManager.getInstance()
+ .getLocaleList().get(0).toLanguageTag();
+ }
+ }
+ }
+ if (selectedLocales == null) {
+ if (ResourcesManager.getInstance().getLocaleList().isEmpty()) {
+ selectedLocales = new String[]{
+ adjustLanguageTag(locales.get(0).toLanguageTag())};
+ } else {
+ selectedLocales = new String[locales.size()];
+ for (int i = 0; i < locales.size(); i++) {
+ selectedLocales[i] = adjustLanguageTag(locales.get(i).toLanguageTag());
}
}
}
@@ -488,7 +508,8 @@
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
+ defaultLocale,
+ selectedLocales,
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5940819..703f165 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1468,6 +1468,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
* <p><b>Units</b>: Frames per second (FPS)</p>
* <p><b>Range of valid values:</b><br>
* Any of the entries in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}</p>
@@ -2140,6 +2147,12 @@
* {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 905f98d..746648b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -887,6 +887,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
* <p><b>Units</b>: Frames per second (FPS)</p>
* <p><b>Range of valid values:</b><br>
* Any of the entries in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}</p>
@@ -2365,6 +2372,12 @@
* {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8f653b3..c0a44b1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -650,8 +650,6 @@
private InlineSuggestionSessionController mInlineSuggestionSessionController;
- private boolean mHideNavBarForKeyboard;
- private boolean mIsAutomotive;
private @NonNull OptionalInt mHandwritingRequestId = OptionalInt.empty();
private InputEventReceiver mHandwritingEventReceiver;
private Handler mHandler;
@@ -1622,7 +1620,7 @@
// shown the first time (cold start).
mSettingsObserver.shouldShowImeWithHardKeyboard();
- mHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
+ final boolean hideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
com.android.internal.R.bool.config_hideNavBarForKeyboard);
initConfigurationTracker();
@@ -1668,7 +1666,7 @@
// screen real estate. When this happens, the IME window should animate from the
// bottom of the screen to reduce the jank that happens from the lack of synchronization
// between the bottom system window and the IME window.
- if (mHideNavBarForKeyboard) {
+ if (hideNavBarForKeyboard) {
window.setDecorFitsSystemWindows(false);
}
}
@@ -2366,9 +2364,7 @@
public void setExtractView(View view) {
mExtractFrame.removeAllViews();
- mExtractFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
+ mExtractFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mExtractView = view;
if (view != null) {
mExtractEditText = view.findViewById(
@@ -2387,7 +2383,7 @@
mExtractAction = null;
}
}
-
+
/**
* Replaces the current candidates view with a new one. You only need to
* call this when dynamically changing the view; normally, you should
@@ -2396,11 +2392,9 @@
*/
public void setCandidatesView(View view) {
mCandidatesFrame.removeAllViews();
- mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
}
-
+
/**
* Replaces the current input view with a new one. You only need to
* call this when dynamically changing the view; normally, you should
@@ -2409,12 +2403,10 @@
*/
public void setInputView(View view) {
mInputFrame.removeAllViews();
- mInputFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ mInputFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
mInputView = view;
}
-
+
/**
* Called by the framework to create the layout for showing extracted text.
* Only called when in fullscreen mode. The returned view hierarchy must
@@ -3448,9 +3440,12 @@
return false;
}
+ /**
+ * Not implemented in this class.
+ */
public void onAppPrivateCommand(String action, Bundle data) {
}
-
+
/**
* Handle a request by the system to toggle the soft input area.
*/
@@ -4092,11 +4087,6 @@
| (isInputViewShown() ? IME_VISIBLE : 0);
}
- private boolean isAutomotive() {
- return getApplicationContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- }
-
/**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index b74bb33..82cdd28 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -30,6 +30,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
/**
@@ -151,6 +152,25 @@
}
/**
+ * Find the intersection between this LocaleList and another
+ * @return a String array of the Locales in both LocaleLists
+ * {@hide}
+ */
+ @NonNull
+ public String[] getIntersection(@NonNull LocaleList other) {
+ List<String> intersection = new ArrayList<>();
+ for (Locale l1 : mList) {
+ for (Locale l2 : other.mList) {
+ if (matchesLanguageAndScript(l2, l1)) {
+ intersection.add(l1.toLanguageTag());
+ break;
+ }
+ }
+ }
+ return intersection.toArray(new String[0]);
+ }
+
+ /**
* Creates a new {@link LocaleList}.
*
* If two or more same locales are passed, the repeated locales will be dropped.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 45b4935..522caac 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11656,6 +11656,21 @@
"accessibility_floating_menu_migration_tooltip_prompt";
/**
+ * For the force dark theme feature which inverts any apps that don't already support dark
+ * theme.
+ *
+ * If true, it will automatically invert any app that is mainly light.
+ *
+ * This is related to the force dark override setting, however it will always force the apps
+ * colors and will ignore any developer hints or opt-out APIs.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED =
+ "accessibility_force_invert_color_enabled";
+
+ /**
* Whether the Adaptive connectivity option is enabled.
*
* @hide
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 1c59742..eb49f41 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -874,7 +874,7 @@
if (camera == 0) return 0;
String8 params8 = camera->getParameters();
- if (params8.isEmpty()) {
+ if (params8.empty()) {
jniThrowRuntimeException(env, "getParameters failed (empty parameters)");
return 0;
}
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 5293c58..3e4c7c7 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -526,7 +526,7 @@
"Failed to read from fd (errno = %#x, message = '%s')",
errno, strerror(errno));
//return;
- } else if (!logLine.isEmpty()) {
+ } else if (!logLine.empty()) {
ALOGD("%s", logLine.string());
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index f04b901..3ee15ab 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -347,14 +347,23 @@
}
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
- jstring locale, jint orientation, jint touchscreen, jint density,
- jint keyboard, jint keyboard_hidden, jint navigation,
- jint screen_width, jint screen_height,
- jint smallest_screen_width_dp, jint screen_width_dp,
- jint screen_height_dp, jint screen_layout, jint ui_mode,
- jint color_mode, jint grammatical_gender, jint major_version) {
+ jstring default_locale, jobjectArray locales, jint orientation,
+ jint touchscreen, jint density, jint keyboard,
+ jint keyboard_hidden, jint navigation, jint screen_width,
+ jint screen_height, jint smallest_screen_width_dp,
+ jint screen_width_dp, jint screen_height_dp, jint screen_layout,
+ jint ui_mode, jint color_mode, jint grammatical_gender,
+ jint major_version) {
ATRACE_NAME("AssetManager::SetConfiguration");
+ const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales);
+
+ // Constants duplicated from Java class android.content.res.Configuration.
+ static const jint kScreenLayoutRoundMask = 0x300;
+ static const jint kScreenLayoutRoundShift = 8;
+
+ std::vector<ResTable_config> configs;
+
ResTable_config configuration;
memset(&configuration, 0, sizeof(configuration));
configuration.mcc = static_cast<uint16_t>(mcc);
@@ -375,25 +384,37 @@
configuration.colorMode = static_cast<uint8_t>(color_mode);
configuration.grammaticalInflection = static_cast<uint8_t>(grammatical_gender);
configuration.sdkVersion = static_cast<uint16_t>(major_version);
-
- if (locale != nullptr) {
- ScopedUtfChars locale_utf8(env, locale);
- CHECK(locale_utf8.c_str() != nullptr);
- configuration.setBcp47Locale(locale_utf8.c_str());
- }
-
- // Constants duplicated from Java class android.content.res.Configuration.
- static const jint kScreenLayoutRoundMask = 0x300;
- static const jint kScreenLayoutRoundShift = 8;
-
// In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
// in C++. We must extract the round qualifier out of the Java screenLayout and put it
// into screenLayout2.
configuration.screenLayout2 =
- static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+ static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
+ if (locale_count > 0) {
+ configs.resize(locale_count, configuration);
+ for (int i = 0; i < locale_count; i++) {
+ jstring locale = (jstring)(env->GetObjectArrayElement(locales, i));
+ ScopedUtfChars locale_utf8(env, locale);
+ CHECK(locale_utf8.c_str() != nullptr);
+ configs[i].setBcp47Locale(locale_utf8.c_str());
+ }
+ } else {
+ configs.push_back(configuration);
+ }
+
+ uint32_t default_locale_int = 0;
+ if (default_locale != nullptr) {
+ ResTable_config config;
+ static_assert(std::is_same_v<decltype(config.locale), decltype(default_locale_int)>);
+ ScopedUtfChars locale_utf8(env, default_locale);
+ CHECK(locale_utf8.c_str() != nullptr);
+ config.setBcp47Locale(locale_utf8.c_str());
+ default_locale_int = config.locale;
+ }
auto assetmanager = LockAndStartAssetManager(ptr);
- assetmanager->SetConfiguration(configuration);
+ assetmanager->SetConfigurations(configs);
+ assetmanager->SetDefaultLocale(default_locale_int);
}
static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
@@ -1498,94 +1519,97 @@
// JNI registration.
static const JNINativeMethod gAssetManagerMethods[] = {
- // AssetManager setup methods.
- {"nativeCreate", "()J", (void*)NativeCreate},
- {"nativeDestroy", "(J)V", (void*)NativeDestroy},
- {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
- {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIIII)V",
- (void*)NativeSetConfiguration},
- {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
- (void*)NativeGetAssignedPackageIdentifiers},
+ // AssetManager setup methods.
+ {"nativeCreate", "()J", (void*)NativeCreate},
+ {"nativeDestroy", "(J)V", (void*)NativeDestroy},
+ {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
+ {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V",
+ (void*)NativeSetConfiguration},
+ {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
+ (void*)NativeGetAssignedPackageIdentifiers},
- // AssetManager file methods.
- {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
- {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
- {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
- {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*)NativeOpenAssetFd},
- {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
- {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*)NativeOpenNonAssetFd},
- {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
- {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
+ // AssetManager file methods.
+ {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
+ {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
+ {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
+ {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenAssetFd},
+ {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
+ {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenNonAssetFd},
+ {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+ {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
- // AssetManager resource methods.
- {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
- {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
- (void*)NativeGetResourceBagValue},
- {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
- {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
- (void*)NativeGetResourceStringArray},
- {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
- {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
- {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
- {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
- {"nativeGetParentThemeIdentifier", "(JI)I",
- (void*)NativeGetParentThemeIdentifier},
+ // AssetManager resource methods.
+ {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I",
+ (void*)NativeGetResourceValue},
+ {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
+ (void*)NativeGetResourceBagValue},
+ {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
+ {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
+ (void*)NativeGetResourceStringArray},
+ {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
+ {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
+ {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
+ {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+ {"nativeGetParentThemeIdentifier", "(JI)I", (void*)NativeGetParentThemeIdentifier},
- // AssetManager resource name/ID methods.
- {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)NativeGetResourceIdentifier},
- {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
- {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
- {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
- {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
- {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
- (void*) NativeSetResourceResolutionLoggingEnabled},
- {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
- (void*) NativeGetLastResourceResolution},
- {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
- {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
- (void*)NativeGetSizeConfigurations},
- {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
- (void*)NativeGetSizeAndUiModeConfigurations},
+ // AssetManager resource name/ID methods.
+ {"nativeGetResourceIdentifier",
+ "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)NativeGetResourceIdentifier},
+ {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
+ {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;",
+ (void*)NativeGetResourcePackageName},
+ {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
+ {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
+ {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
+ (void*)NativeSetResourceResolutionLoggingEnabled},
+ {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
+ (void*)NativeGetLastResourceResolution},
+ {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
+ {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeConfigurations},
+ {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeAndUiModeConfigurations},
- // Style attribute related methods.
- {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
- {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
- {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
- {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
+ // Style attribute related methods.
+ {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
+ {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
+ {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
+ {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
- // Theme related methods.
- {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
- {"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction},
- {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
- {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},
+ // Theme related methods.
+ {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
+ {"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction},
+ {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
+ {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},
- {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
- {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
- (void*)NativeThemeGetAttributeValue},
- {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
- {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
+ {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
+ {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
+ (void*)NativeThemeGetAttributeValue},
+ {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
+ {"nativeThemeGetChangingConfigurations", "(J)I",
+ (void*)NativeThemeGetChangingConfigurations},
- // AssetInputStream methods.
- {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
- {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
- {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
- {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
- {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
- {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
+ // AssetInputStream methods.
+ {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
+ {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
+ {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
+ {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
+ {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
+ {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
- // System/idmap related methods.
- {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
- (void*)NativeGetOverlayableMap},
- {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
- (void*)NativeGetOverlayablesToString},
+ // System/idmap related methods.
+ {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
+ (void*)NativeGetOverlayableMap},
+ {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
+ (void*)NativeGetOverlayablesToString},
- // Global management/debug methods.
- {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
- {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
- {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
+ // Global management/debug methods.
+ {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
+ {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
+ {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
};
int register_android_content_AssetManager(JNIEnv* env) {
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ed0081c..ad88092 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -97,6 +97,7 @@
optional SettingProto accessibility_magnification_joystick_enabled = 50 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Settings for font scaling
optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f45499a..e54347f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3537,11 +3537,11 @@
<attr name="preferKeepClear" format="boolean" />
<!-- <p>Whether or not the auto handwriting initiation is enabled in this View.
- <p>For a view with active {@link android.view.inputmethod.InputConnection},
- if auto handwriting initiation is enabled stylus movement within its view boundary
+ <p>For a view with an active {@link android.view.inputmethod.InputConnection},
+ if auto handwriting initiation is enabled, stylus movement within its view boundary
will automatically trigger the handwriting mode.
- <p>This is true by default.
- See {@link android.view.View#setAutoHandwritingEnabled}. -->
+ See {@link android.view.View#setAutoHandwritingEnabled}.
+ <p>The default value of this flag is configurable by the device manufacturer. -->
<attr name="autoHandwritingEnabled" format="boolean" />
<!-- <p>The amount of offset that is applied to the left edge of the view's stylus
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index 54a3817..87e4a42 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -39,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,6 +55,7 @@
* Due to how the classes are structured, we have to test it in a somewhat roundabout way. We're
* mocking out the contentProvider and are handcrafting very specific Bundles to answer the queries.
*/
+@Ignore("b/297724333")
@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -233,6 +235,7 @@
mConfigsCacheGenerationStore.close();
}
+ @Ignore("b/297724333")
@Test
public void testCaching_singleNamespace() throws Exception {
HashMap<String, String> keyValues = new HashMap<>();
@@ -270,6 +273,7 @@
assertThat(cachedKeyValues2).containsExactlyEntriesIn(keyValues);
}
+ @Ignore("b/297724333")
@Test
public void testCaching_multipleNamespaces() throws Exception {
HashMap<String, String> keyValues = new HashMap<>();
@@ -309,6 +313,7 @@
assertThat(cachedKeyValues2).containsExactlyEntriesIn(keyValues2);
}
+ @Ignore("b/297724333")
@Test
public void testCaching_emptyNamespace() throws Exception {
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
@@ -325,6 +330,7 @@
assertThat(cachedKeyValues).isEmpty();
}
+ @Ignore("b/297724333")
@Test
public void testCaching_singleSetting() throws Exception {
Settings.Secure.putString(mMockContentResolver, SETTING, "a");
@@ -355,6 +361,7 @@
assertThat(cachedValue2).isEqualTo("b");
}
+ @Ignore("b/297724333")
@Test
public void testCaching_multipleSettings() throws Exception {
Settings.Secure.putString(mMockContentResolver, SETTING, "a");
@@ -385,6 +392,7 @@
assertThat(cachedValue2).isEqualTo("b");
}
+ @Ignore("b/297724333")
@Test
public void testCaching_unsetSetting() throws Exception {
String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt
index fd000ee..a8743fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt
@@ -21,7 +21,6 @@
import android.graphics.PointF
import android.util.Size
import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipDisplayLayoutState
class LegacySizeSpecSource(
private val context: Context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhonePipKeepClearAlgorithm.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhonePipKeepClearAlgorithm.java
index 2d34035..133242d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhonePipKeepClearAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.wm.shell.pip.phone;
+package com.android.wm.shell.common.pip;
import android.content.Context;
import android.content.res.Resources;
@@ -24,9 +24,6 @@
import android.view.Gravity;
import com.android.wm.shell.R;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import java.util.Set;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
index c563068..18c7bdd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
@@ -21,7 +21,6 @@
import android.os.SystemProperties
import android.util.Size
import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipDisplayLayoutState
import java.io.PrintWriter
class PhoneSizeSpecSource(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 4fef672..a9f687f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +28,6 @@
import android.view.Gravity;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.pip.SizeSpecSource;
import java.io.PrintWriter;
@@ -202,7 +200,8 @@
*
* @return {@code false} if the given source is too small to use for the entering animation.
*/
- static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint, Rect destinationBounds) {
+ public static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint,
+ Rect destinationBounds) {
return sourceRectHint != null
&& sourceRectHint.width() > destinationBounds.width()
&& sourceRectHint.height() > destinationBounds.height();
@@ -224,7 +223,7 @@
}
/**
- * @return whether the given {@param aspectRatio} is valid.
+ * @return whether the given aspectRatio is valid.
*/
public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
return Float.compare(mMinAspectRatio, aspectRatio) <= 0
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 279ffc5..3b32b6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -36,7 +36,6 @@
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -314,8 +313,11 @@
return mPipDisplayLayoutState.getDisplayLayout();
}
+ /**
+ * Clears the PiP re-entry state.
+ */
@VisibleForTesting
- void clearReentryState() {
+ public void clearReentryState() {
mPipReentryState = null;
}
@@ -400,11 +402,18 @@
mNamedUnrestrictedKeepClearAreas.remove(name);
}
+
+ /**
+ * @return restricted keep clear areas.
+ */
@NonNull
public Set<Rect> getRestrictedKeepClearAreas() {
return mRestrictedKeepClearAreas;
}
+ /**
+ * @return unrestricted keep clear areas.
+ */
@NonNull
public Set<Rect> getUnrestrictedKeepClearAreas() {
if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
@@ -561,7 +570,11 @@
}
}
- static final class PipReentryState {
+ /**
+ * Represents the state of pip to potentially restore upon reentry.
+ */
+ @VisibleForTesting
+ public static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
private final @Nullable Size mSize;
@@ -573,11 +586,11 @@
}
@Nullable
- Size getSize() {
+ public Size getSize() {
return mSize;
}
- float getSnapFraction() {
+ public float getSnapFraction() {
return mSnapFraction;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDisplayLayoutState.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDisplayLayoutState.java
index 4aa260b..ed42117 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDisplayLayoutState.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import static com.android.wm.shell.common.pip.PipUtils.dpToPx;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipKeepClearAlgorithmInterface.java
similarity index 95%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipKeepClearAlgorithmInterface.java
index 5045cf9..954233c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipKeepClearAlgorithmInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.graphics.Rect;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPinchResizingAlgorithm.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPinchResizingAlgorithm.java
index 23153be72..02b3a88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPinchResizingAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.pip.phone;
+package com.android.wm.shell.common.pip;
import android.graphics.Point;
import android.graphics.PointF;
@@ -35,7 +35,7 @@
private final PointF mTmpLastCentroid = new PointF();
/**
- * Updates {@param resizeBoundsOut} with the new bounds of the PIP, and returns the angle in
+ * Updates resizeBoundsOut with the new bounds of the PIP, and returns the angle in
* degrees that the PIP should be rotated.
*/
public float calculateBoundsAndAngle(PointF downPoint, PointF downSecondPoint,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipSnapAlgorithm.java
similarity index 84%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipSnapAlgorithm.java
index dd30137..007052e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipSnapAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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,11 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
import android.graphics.Rect;
@@ -39,14 +39,14 @@
}
/**
- * @return returns a fraction that describes where along the {@param movementBounds} the
- * {@param stackBounds} are. If the {@param stackBounds} are not currently on the
- * {@param movementBounds} exactly, then they will be snapped to the movement bounds.
+ * @return returns a fraction that describes where along the movementBounds the
+ * stackBounds are. If the stackBounds are not currently on the
+ * movementBounds exactly, then they will be snapped to the movement bounds.
* stashType dictates whether the PiP is stashed (off-screen) or not. If
* that's the case, we will have to do some math to calculate the snap fraction
* correctly.
*
- * The fraction is defined in a clockwise fashion against the {@param movementBounds}:
+ * The fraction is defined in a clockwise fashion against the movementBounds:
*
* 0 1
* 4 +---+ 1
@@ -58,10 +58,10 @@
@PipBoundsState.StashType int stashType) {
final Rect tmpBounds = new Rect();
snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds, stashType);
- final float widthFraction = (float) (tmpBounds.left - movementBounds.left) /
- movementBounds.width();
- final float heightFraction = (float) (tmpBounds.top - movementBounds.top) /
- movementBounds.height();
+ final float widthFraction = (float) (tmpBounds.left - movementBounds.left)
+ / movementBounds.width();
+ final float heightFraction = (float) (tmpBounds.top - movementBounds.top)
+ / movementBounds.height();
if (tmpBounds.top == movementBounds.top) {
return widthFraction;
} else if (tmpBounds.left == movementBounds.right) {
@@ -74,10 +74,10 @@
}
/**
- * Moves the {@param stackBounds} along the {@param movementBounds} to the given snap fraction.
+ * Moves the stackBounds along the movementBounds to the given snap fraction.
* See {@link #getSnapFraction(Rect, Rect)}.
*
- * The fraction is define in a clockwise fashion against the {@param movementBounds}:
+ * The fraction is define in a clockwise fashion against the movementBounds:
*
* 0 1
* 4 +---+ 1
@@ -122,11 +122,11 @@
}
/**
- * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
- * the new bounds out to {@param boundsOut}.
+ * Snaps the stackBounds to the closest edge of the movementBounds and writes
+ * the new bounds out to boundsOut.
*/
@VisibleForTesting
- void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
+ public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
@PipBoundsState.StashType int stashType) {
int leftEdge = stackBounds.left;
if (stashType == STASH_TYPE_LEFT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index e28b8d7..1c2cee5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -59,8 +59,15 @@
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
+import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
@@ -354,6 +361,42 @@
return new PipMediaController(context, mainHandler);
}
+ @WMSingleton
+ @Provides
+ static SizeSpecSource provideSizeSpecSource(Context context,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PhoneSizeSpecSource(context, pipDisplayLayoutState);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipBoundsState providePipBoundsState(Context context,
+ SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
+ }
+
+
+ @WMSingleton
+ @Provides
+ static PipSnapAlgorithm providePipSnapAlgorithm() {
+ return new PipSnapAlgorithm();
+ }
+
+ @WMSingleton
+ @Provides
+ static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
+ return new PhonePipKeepClearAlgorithm(context);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
+ PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) {
+ return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
+ pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource);
+ }
//
// Bubbles (optional feature)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 4e92ca1..ba882c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -30,9 +30,13 @@
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
@@ -41,17 +45,12 @@
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
@@ -119,35 +118,6 @@
}
}
- @WMSingleton
- @Provides
- static PipBoundsState providePipBoundsState(Context context,
- SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
- return new PipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
- }
-
- @WMSingleton
- @Provides
- static PipSnapAlgorithm providePipSnapAlgorithm() {
- return new PipSnapAlgorithm();
- }
-
- @WMSingleton
- @Provides
- static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
- return new PhonePipKeepClearAlgorithm(context);
- }
-
- @WMSingleton
- @Provides
- static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
- PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
- PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) {
- return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
- pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource);
- }
-
// Handler is used by Icon.loadDrawableAsync
@WMSingleton
@Provides
@@ -213,13 +183,6 @@
@WMSingleton
@Provides
- static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
- pipSurfaceTransactionHelper) {
- return new PipAnimationController(pipSurfaceTransactionHelper);
- }
-
- @WMSingleton
- @Provides
static PipTransition providePipTransition(Context context,
ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
@@ -235,13 +198,6 @@
@WMSingleton
@Provides
- static SizeSpecSource provideSizeSpecSource(Context context,
- PipDisplayLayoutState pipDisplayLayoutState) {
- return new PhoneSizeSpecSource(context, pipDisplayLayoutState);
- }
-
- @WMSingleton
- @Provides
static PipAppOpsListener providePipAppOpsListener(Context context,
PipTouchHandler pipTouchHandler,
@ShellMainThread ShellExecutor mainExecutor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
index c4ca501..b42372b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
@@ -19,6 +19,7 @@
import android.content.Context;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import dagger.Module;
@@ -35,4 +36,11 @@
static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
return new PipSurfaceTransactionHelper(context);
}
+
+ @WMSingleton
+ @Provides
+ static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+ pipSurfaceTransactionHelper) {
+ return new PipAnimationController(pipSurfaceTransactionHelper);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 8dec4ea..af97cf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -16,11 +16,16 @@
package com.android.wm.shell.dagger.pip;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip2.PipTransition;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import dagger.Module;
import dagger.Provides;
@@ -33,8 +38,12 @@
public abstract class Pip2Module {
@WMSingleton
@Provides
- @Nullable
- static PipTransition providePipTransition() {
- return null;
+ static PipTransition providePipTransition(@NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
+ PipBoundsAlgorithm pipBoundsAlgorithm) {
+ return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
+ pipBoundsAlgorithm);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 9c9364e..570f0a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.dagger.pip;
-import android.annotation.Nullable;
-
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.PipTransitionController;
@@ -38,8 +36,8 @@
@Provides
static PipTransitionController providePipTransitionController(
com.android.wm.shell.pip.PipTransition legacyPipTransition,
- @Nullable com.android.wm.shell.pip2.PipTransition newPipTransition) {
- if (PipUtils.isPip2ExperimentEnabled() && newPipTransition != null) {
+ com.android.wm.shell.pip2.PipTransition newPipTransition) {
+ if (PipUtils.isPip2ExperimentEnabled()) {
return newPipTransition;
} else {
return legacyPipTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index a6ff9ec..a9675f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -30,16 +30,15 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -131,15 +130,9 @@
@WMSingleton
@Provides
- static PipSnapAlgorithm providePipSnapAlgorithm() {
- return new PipSnapAlgorithm();
- }
-
- @WMSingleton
- @Provides
static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) {
+ PipDisplayLayoutState pipDisplayLayoutState, LegacySizeSpecSource sizeSpecSource) {
return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm,
pipDisplayLayoutState, sizeSpecSource);
}
@@ -147,13 +140,13 @@
@WMSingleton
@Provides
static TvPipBoundsState provideTvPipBoundsState(Context context,
- SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
+ LegacySizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
return new TvPipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
}
@WMSingleton
@Provides
- static SizeSpecSource provideSizeSpecSource(Context context,
+ static LegacySizeSpecSource provideSizeSpecSource(Context context,
PipDisplayLayoutState pipDisplayLayoutState) {
return new LegacySizeSpecSource(context, pipDisplayLayoutState);
}
@@ -200,13 +193,6 @@
@WMSingleton
@Provides
- static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
- pipSurfaceTransactionHelper) {
- return new PipAnimationController(pipSurfaceTransactionHelper);
- }
-
- @WMSingleton
- @Provides
static PipTransitionState providePipTransitionState() {
return new PipTransitionState();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index ed9ff1c..9e8f9c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -82,6 +82,9 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.phone.PipMotionHelper;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 83e03dc..e3922d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -64,6 +64,9 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -89,6 +92,7 @@
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
+ private final PipAnimationController mPipAnimationController;
private @PipAnimationController.AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private SurfaceControl.Transaction mFinishTransaction;
@@ -137,10 +141,11 @@
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController);
+ pipBoundsAlgorithm);
mContext = context;
mPipTransitionState = pipTransitionState;
mPipDisplayLayoutState = pipDisplayLayoutState;
+ mPipAnimationController = pipAnimationController;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
@@ -148,6 +153,13 @@
}
@Override
+ protected void onInit() {
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ mTransitions.addHandler(this);
+ }
+ }
+
+ @Override
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
if (destinationBounds != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 64bba67..20c57fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -38,7 +38,8 @@
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -52,7 +53,6 @@
*/
public abstract class PipTransitionController implements Transitions.TransitionHandler {
- protected final PipAnimationController mPipAnimationController;
protected final PipBoundsAlgorithm mPipBoundsAlgorithm;
protected final PipBoundsState mPipBoundsState;
protected final ShellTaskOrganizer mShellTaskOrganizer;
@@ -135,22 +135,18 @@
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
- PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipAnimationController pipAnimationController) {
+ PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm) {
mPipBoundsState = pipBoundsState;
mPipMenuController = pipMenuController;
mShellTaskOrganizer = shellTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
- mPipAnimationController = pipAnimationController;
mTransitions = transitions;
- if (!PipUtils.isPip2ExperimentEnabled()) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- shellInit.addInitCallback(this::onInit, this);
- }
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ shellInit.addInitCallback(this::onInit, this);
}
}
- private void onInit() {
+ protected void onInit() {
mTransitions.addHandler(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index cc182ba..7606526 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -38,10 +38,10 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 8c2879e..118ad9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -15,7 +15,7 @@
*/
package com.android.wm.shell.pip.phone;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
import android.annotation.NonNull;
import android.content.Context;
@@ -36,8 +36,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index ddea574..1064867 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -76,7 +76,12 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
@@ -85,12 +90,7 @@
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
index d7d335b..1b1ebc3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.graphics.Rect;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipBoundsState;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 8f0a8e1..c708b86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -20,10 +20,10 @@
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_DISMISS;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -41,8 +41,8 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.common.pip.PipAppOpsListener;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
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 4e687dd..e5f9fdc 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
@@ -46,10 +46,11 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index cf54a71..2ce4fb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -18,10 +18,10 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -50,12 +50,12 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index cd58ff4..a48e969f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -36,11 +36,11 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 8d4a384..8a215b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.pip.tv;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index d11f4d5..2b3a93e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -29,10 +29,10 @@
import android.view.Gravity;
import android.view.View;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 5f5d8ad..72115fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -48,11 +48,11 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
index a94bd6e..93f6826 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
@@ -21,12 +21,12 @@
import android.graphics.Rect
import android.util.Size
import android.view.Gravity
-import com.android.wm.shell.pip.PipBoundsState
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
+import com.android.wm.shell.common.pip.PipBoundsState
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_BOTTOM
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_TOP
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -54,11 +54,11 @@
* the unstash timeout if already stashed.
*/
data class Placement(
- val bounds: Rect,
- val anchorBounds: Rect,
- @PipBoundsState.StashType val stashType: Int = STASH_TYPE_NONE,
- val unstashDestinationBounds: Rect? = null,
- val triggerStash: Boolean = false
+ val bounds: Rect,
+ val anchorBounds: Rect,
+ @PipBoundsState.StashType val stashType: Int = STASH_TYPE_NONE,
+ val unstashDestinationBounds: Rect? = null,
+ val triggerStash: Boolean = false
) {
/** Bounds to use if the PiP should not be stashed. */
fun getUnstashedBounds() = unstashDestinationBounds ?: bounds
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 6720804..f315afb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -25,12 +25,12 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index d3253a5..f24b2b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -21,8 +21,8 @@
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
index 8ab85d0..b8e4c04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
@@ -17,27 +17,65 @@
package com.android.wm.shell.pip2;
import android.annotation.NonNull;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.Nullable;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
/** Placeholder, for demonstrate purpose only. */
-public abstract class PipTransition extends PipTransitionController {
+public class PipTransition extends PipTransitionController {
public PipTransition(
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
- PipBoundsAlgorithm pipBoundsAlgorithm,
- PipAnimationController pipAnimationController) {
+ PipBoundsAlgorithm pipBoundsAlgorithm) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController);
+ pipBoundsAlgorithm);
}
+
+ @Override
+ protected void onInit() {
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ mTransitions.addHandler(this);
+ }
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ return false;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {}
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index bf1b7f9..46259a8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -33,6 +33,11 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.SizeSpecSource;
import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 4341c4c..d34e27b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -36,6 +36,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
index b9226d2..ac13d7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
@@ -25,6 +25,8 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 248d665..4e2b7f6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -53,6 +53,11 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
index cc9e26b..8c7b47e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
@@ -29,8 +29,9 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
index 024cba3..3d5cd69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
@@ -33,8 +33,8 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import org.junit.After;
import org.junit.Assert;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 911f5e1..4eb5193 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -55,15 +55,16 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
index 8ce3ca4..0f8db85 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
@@ -29,7 +29,7 @@
import android.testing.AndroidTestingRunner;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipBoundsState;
import org.junit.Assert;
import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 12b4f3e..6777a5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -37,13 +37,13 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 314f195d..9aaabd1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -33,13 +33,13 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 7370ed7..94f2b91 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -25,7 +25,7 @@
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT
import com.android.wm.shell.pip.tv.TvPipBoundsController.POSITION_DEBOUNCE_TIMEOUT_MILLIS
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 256610b..974539f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -27,9 +27,9 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
index aedf65d..998060f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
@@ -22,10 +22,10 @@
import android.util.Size
import android.view.Gravity
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_BOTTOM
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_TOP
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index b1ef4e5..5ffec34 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -91,12 +91,17 @@
StringPoolRef entry_string_ref;
};
-AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration)
- : configuration_(configuration) {
+AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) {
+ configurations_.push_back(configuration);
+
// Don't invalidate caches here as there's nothing cached yet.
SetApkAssets(apk_assets, false);
}
+AssetManager2::AssetManager2() {
+ configurations_.resize(1);
+}
+
bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
BuildDynamicRefTable(apk_assets);
RebuildFilterList();
@@ -421,9 +426,16 @@
return false;
}
-void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
- const int diff = configuration_.diff(configuration);
- configuration_ = configuration;
+void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
+ int diff = 0;
+ if (configurations_.size() != configurations.size()) {
+ diff = -1;
+ } else {
+ for (int i = 0; i < configurations_.size(); i++) {
+ diff |= configurations_[i].diff(configurations[i]);
+ }
+ }
+ configurations_ = std::move(configurations);
if (diff) {
RebuildFilterList();
@@ -620,16 +632,6 @@
auto op = StartOperation();
- // Might use this if density_override != 0.
- ResTable_config density_override_config;
-
- // Select our configuration or generate a density override configuration.
- const ResTable_config* desired_config = &configuration_;
- if (density_override != 0 && density_override != configuration_.density) {
- density_override_config = configuration_;
- density_override_config.density = density_override;
- desired_config = &density_override_config;
- }
// Retrieve the package group from the package id of the resource id.
if (UNLIKELY(!is_valid_resid(resid))) {
@@ -648,119 +650,160 @@
}
const PackageGroup& package_group = package_groups_[package_idx];
- auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
- stop_at_first_match, ignore_configuration);
- if (UNLIKELY(!result.has_value())) {
- return base::unexpected(result.error());
- }
+ std::optional<FindEntryResult> final_result;
+ bool final_has_locale = false;
+ bool final_overlaid = false;
+ for (auto & config : configurations_) {
+ // Might use this if density_override != 0.
+ ResTable_config density_override_config;
- bool overlaid = false;
- if (!stop_at_first_match && !ignore_configuration) {
- const auto& assets = GetApkAssets(result->cookie);
- if (!assets) {
- ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
- return base::unexpected(std::nullopt);
+ // Select our configuration or generate a density override configuration.
+ const ResTable_config* desired_config = &config;
+ if (density_override != 0 && density_override != config.density) {
+ density_override_config = config;
+ density_override_config.density = density_override;
+ desired_config = &density_override_config;
}
- if (!assets->IsLoader()) {
- for (const auto& id_map : package_group.overlays_) {
- auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
- if (!overlay_entry) {
- // No id map entry exists for this target resource.
- continue;
- }
- if (overlay_entry.IsInlineValue()) {
- // The target resource is overlaid by an inline value not represented by a resource.
- ConfigDescription best_frro_config;
- Res_value best_frro_value;
- bool frro_found = false;
- for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
- if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
- && config.match(*desired_config)) {
- frro_found = true;
- best_frro_config = config;
- best_frro_value = value;
- }
- }
- if (!frro_found) {
+
+ auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ stop_at_first_match, ignore_configuration);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
+ }
+ bool overlaid = false;
+ if (!stop_at_first_match && !ignore_configuration) {
+ const auto& assets = GetApkAssets(result->cookie);
+ if (!assets) {
+ ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
+ return base::unexpected(std::nullopt);
+ }
+ if (!assets->IsLoader()) {
+ for (const auto& id_map : package_group.overlays_) {
+ auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
+ if (!overlay_entry) {
+ // No id map entry exists for this target resource.
continue;
}
- result->entry = best_frro_value;
+ if (overlay_entry.IsInlineValue()) {
+ // The target resource is overlaid by an inline value not represented by a resource.
+ ConfigDescription best_frro_config;
+ Res_value best_frro_value;
+ bool frro_found = false;
+ for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
+ if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
+ && config.match(*desired_config)) {
+ frro_found = true;
+ best_frro_config = config;
+ best_frro_value = value;
+ }
+ }
+ if (!frro_found) {
+ continue;
+ }
+ result->entry = best_frro_value;
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ result->cookie = id_map.cookie;
+
+ if (UNLIKELY(logging_enabled)) {
+ last_resolution_.steps.push_back(Resolution::Step{
+ Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
+ if (auto path = assets->GetPath()) {
+ const std::string overlay_path = path->data();
+ if (IsFabricatedOverlay(overlay_path)) {
+ // FRRO don't have package name so we use the creating package here.
+ String8 frro_name = String8("FRRO");
+ // Get the first part of it since the expected one should be like
+ // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
+ // under /data/resource-cache/.
+ const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
+ const size_t end = name.find('-');
+ if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
+ frro_name.append(base::StringPrintf(" created by %s",
+ name.substr(0 /* pos */,
+ end).c_str()).c_str());
+ }
+ last_resolution_.best_package_name = frro_name;
+ } else {
+ last_resolution_.best_package_name = result->package_name->c_str();
+ }
+ }
+ overlaid = true;
+ }
+ continue;
+ }
+
+ auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(overlay_result))) {
+ return base::unexpected(overlay_result.error());
+ }
+ if (!overlay_result.has_value()) {
+ continue;
+ }
+
+ if (!overlay_result->config.isBetterThan(result->config, desired_config)
+ && overlay_result->config.compare(result->config) != 0) {
+ // The configuration of the entry for the overlay must be equal to or better than the
+ // target configuration to be chosen as the better value.
+ continue;
+ }
+
+ result->cookie = overlay_result->cookie;
+ result->entry = overlay_result->entry;
+ result->config = overlay_result->config;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- result->cookie = id_map.cookie;
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
- if (auto path = assets->GetPath()) {
- const std::string overlay_path = path->data();
- if (IsFabricatedOverlay(overlay_path)) {
- // FRRO don't have package name so we use the creating package here.
- String8 frro_name = String8("FRRO");
- // Get the first part of it since the expected one should be like
- // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
- // under /data/resource-cache/.
- const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
- const size_t end = name.find('-');
- if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
- frro_name.append(base::StringPrintf(" created by %s",
- name.substr(0 /* pos */,
- end).c_str()).c_str());
- }
- last_resolution_.best_package_name = frro_name;
- } else {
- last_resolution_.best_package_name = result->package_name->c_str();
- }
- }
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
+ overlay_result->config.toString()});
+ last_resolution_.best_package_name =
+ overlay_result->package_name->c_str();
overlaid = true;
}
- continue;
- }
-
- auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
- false /* stop_at_first_match */,
- false /* ignore_configuration */);
- if (UNLIKELY(IsIOError(overlay_result))) {
- return base::unexpected(overlay_result.error());
- }
- if (!overlay_result.has_value()) {
- continue;
- }
-
- if (!overlay_result->config.isBetterThan(result->config, desired_config)
- && overlay_result->config.compare(result->config) != 0) {
- // The configuration of the entry for the overlay must be equal to or better than the target
- // configuration to be chosen as the better value.
- continue;
- }
-
- result->cookie = overlay_result->cookie;
- result->entry = overlay_result->entry;
- result->config = overlay_result->config;
- result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
-
- if (UNLIKELY(logging_enabled)) {
- last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
- overlay_result->config.toString()});
- last_resolution_.best_package_name =
- overlay_result->package_name->c_str();
- overlaid = true;
}
}
}
+
+ bool has_locale = false;
+ if (result->config.locale == 0) {
+ if (default_locale_ != 0) {
+ ResTable_config conf;
+ conf.locale = default_locale_;
+ // Since we know conf has a locale and only a locale, match will tell us if that locale
+ // matches
+ has_locale = conf.match(config);
+ }
+ } else {
+ has_locale = true;
+ }
+
+ // if we don't have a result yet
+ if (!final_result ||
+ // or this config is better before the locale than the existing result
+ result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
+ // or the existing config isn't better before locale and this one specifies a locale
+ // whereas the existing one doesn't
+ (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
+ && has_locale && !final_has_locale)) {
+ final_result = result.value();
+ final_overlaid = overlaid;
+ final_has_locale = has_locale;
+ }
}
if (UNLIKELY(logging_enabled)) {
- last_resolution_.cookie = result->cookie;
- last_resolution_.type_string_ref = result->type_string_ref;
- last_resolution_.entry_string_ref = result->entry_string_ref;
- last_resolution_.best_config_name = result->config.toString();
- if (!overlaid) {
- last_resolution_.best_package_name = result->package_name->c_str();
+ last_resolution_.cookie = final_result->cookie;
+ last_resolution_.type_string_ref = final_result->type_string_ref;
+ last_resolution_.entry_string_ref = final_result->entry_string_ref;
+ last_resolution_.best_config_name = final_result->config.toString();
+ if (!final_overlaid) {
+ last_resolution_.best_package_name = final_result->package_name->c_str();
}
}
- return result;
+ return *final_result;
}
base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
@@ -778,8 +821,10 @@
// If `desired_config` is not the same as the set configuration or the caller will accept a value
// from any configuration, then we cannot use our filtered list of types since it only it contains
// types matched to the set configuration.
- const bool use_filtered = !ignore_configuration && &desired_config == &configuration_;
-
+ const bool use_filtered = !ignore_configuration && std::find_if(
+ configurations_.begin(), configurations_.end(),
+ [&desired_config](auto& value) { return &desired_config == &value; })
+ != configurations_.end();
const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
@@ -934,10 +979,22 @@
}
std::stringstream log_stream;
- log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
- "\tFor config - %s", resid, resource_name_string.c_str(),
- configuration_.toString().c_str());
-
+ if (configurations_.size() == 1) {
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
+ "\tFor config - %s", resid, resource_name_string.c_str(),
+ configurations_[0].toString().c_str());
+ } else {
+ ResTable_config conf = configurations_[0];
+ conf.clearLocale();
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales",
+ resid, resource_name_string.c_str(), conf.toString().c_str());
+ char str[40];
+ str[0] = '\0';
+ for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) {
+ iter->getBcp47Locale(str);
+ log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : "");
+ }
+ }
for (const Resolution::Step& step : last_resolution_.steps) {
constexpr static std::array kStepStrings = {
"Found initial",
@@ -1427,11 +1484,14 @@
package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
- if (type_entry.config.match(configuration_)) {
- if (!group) {
- group = &package.filtered_configs_.editItemAt(type_id - 1);
+ for (auto & config : configurations_) {
+ if (type_entry.config.match(config)) {
+ if (!group) {
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
+ }
+ group->type_entries.push_back(&type_entry);
+ break;
}
- group->type_entries.push_back(&type_entry);
}
}
});
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 112058f..ec14316 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2568,6 +2568,22 @@
return false;
}
+bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
+ const ResTable_config* requested) const {
+ if (requested) {
+ if (imsi || o.imsi) {
+ if ((mcc != o.mcc) && requested->mcc) {
+ return (mcc);
+ }
+
+ if ((mnc != o.mnc) && requested->mnc) {
+ return (mnc);
+ }
+ }
+ }
+ return false;
+}
+
bool ResTable_config::isBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index f611d0d..d9ff35b 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -100,7 +100,7 @@
using ApkAssetsWPtr = wp<const ApkAssets>;
using ApkAssetsList = std::span<const ApkAssetsPtr>;
- AssetManager2() = default;
+ AssetManager2();
explicit AssetManager2(AssetManager2&& other) = default;
AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration);
@@ -156,10 +156,14 @@
// Sets/resets the configuration for this AssetManager. This will cause all
// caches that are related to the configuration change to be invalidated.
- void SetConfiguration(const ResTable_config& configuration);
+ void SetConfigurations(std::vector<ResTable_config> configurations);
- inline const ResTable_config& GetConfiguration() const {
- return configuration_;
+ inline const std::vector<ResTable_config>& GetConfigurations() const {
+ return configurations_;
+ }
+
+ inline void SetDefaultLocale(uint32_t default_locale) {
+ default_locale_ = default_locale;
}
// Returns all configurations for which there are resources defined, or an I/O error if reading
@@ -465,9 +469,11 @@
// without taking too much memory.
std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
- // The current configuration set for this AssetManager. When this changes, cached resources
+ uint32_t default_locale_;
+
+ // The current configurations set for this AssetManager. When this changes, cached resources
// may need to be purged.
- ResTable_config configuration_ = {};
+ std::vector<ResTable_config> configurations_;
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 6de1d1e..fdb3551 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1375,6 +1375,8 @@
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
+ bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
+
String8 toString() const;
};
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 6fae72a..2caa98c 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -228,10 +228,12 @@
ResTable_config config;
memset(&config, 0, sizeof(config));
+ std::vector<ResTable_config> configs;
+ configs.push_back(config);
while (state.KeepRunning()) {
- config.sdkVersion = ~config.sdkVersion;
- assets.SetConfiguration(config);
+ configs[0].sdkVersion = ~configs[0].sdkVersion;
+ assets.SetConfigurations(configs);
}
}
BENCHMARK(BM_AssetManagerSetConfigurationFramework);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index df3fa02..c62f095 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -113,7 +113,7 @@
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -137,7 +137,7 @@
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -466,10 +466,10 @@
TEST_F(AssetManager2Test, DensityOverride) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_});
- assetmanager.SetConfiguration({
+ assetmanager.SetConfigurations({{
.density = ResTable_config::DENSITY_XHIGH,
.sdkVersion = 21,
- });
+ }});
auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
ASSERT_TRUE(value.has_value());
@@ -721,7 +721,7 @@
ResTable_config desired_config;
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
assetmanager.SetResourceResolutionLoggingEnabled(false);
@@ -736,7 +736,7 @@
ResTable_config desired_config;
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto result = assetmanager.GetLastResourceResolution();
@@ -751,7 +751,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -774,7 +774,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -796,7 +796,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -817,7 +817,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({overlayable_assets_});
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index b97dd96..8b883f4 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -66,7 +66,7 @@
AssetManager2 assetmanager;
assetmanager.SetApkAssets(apk_assets);
if (config != nullptr) {
- assetmanager.SetConfiguration(*config);
+ assetmanager.SetConfigurations({*config});
}
while (state.KeepRunning()) {
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index e08a6a7..181d141 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -260,7 +260,7 @@
ResTable_config night{};
night.uiMode = ResTable_config::UI_MODE_NIGHT_YES;
night.version = 8u;
- am_night.SetConfiguration(night);
+ am_night.SetConfigurations({night});
auto theme = am.NewTheme();
{
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index a17f2f7..7aef7a5 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -203,10 +203,9 @@
if (advances) {
advancesArray.reset(new jfloat[count]);
}
- minikin::MinikinRect bounds;
const float advance = MinikinUtils::measureText(
paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count,
- contextCount, advancesArray.get(), &bounds);
+ contextCount, advancesArray.get(), nullptr);
if (advances) {
env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 9695e6f..c41cd04 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -63,10 +63,10 @@
std::shared_ptr<PointerController> PointerController::create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController) {
+ SpriteController& spriteController, bool enabled) {
// using 'new' to access non-public constructor
std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
- new PointerController(policy, looper, spriteController));
+ new PointerController(policy, looper, spriteController, enabled));
/*
* Now we need to hook up the constructed PointerController object to its callbacks.
@@ -85,9 +85,10 @@
}
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, SpriteController& spriteController)
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled)
: PointerController(
- policy, looper, spriteController,
+ policy, looper, spriteController, enabled,
[](const sp<android::gui::WindowInfosListener>& listener) {
SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
},
@@ -97,9 +98,10 @@
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper, SpriteController& spriteController,
- WindowListenerConsumer registerListener,
+ bool enabled, WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener)
- : mContext(policy, looper, spriteController, *this),
+ : mEnabled(enabled),
+ mContext(policy, looper, spriteController, *this),
mCursorController(mContext),
mDisplayInfoListener(sp<DisplayInfoListener>::make(this)),
mUnregisterWindowInfosListener(std::move(unregisterListener)) {
@@ -119,10 +121,14 @@
}
std::optional<FloatRect> PointerController::getBounds() const {
+ if (!mEnabled) return {};
+
return mCursorController.getBounds();
}
void PointerController::move(float deltaX, float deltaY) {
+ if (!mEnabled) return;
+
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
{
@@ -134,6 +140,8 @@
}
void PointerController::setPosition(float x, float y) {
+ if (!mEnabled) return;
+
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
{
@@ -145,6 +153,11 @@
}
FloatPoint PointerController::getPosition() const {
+ if (!mEnabled) {
+ return FloatPoint{AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ }
+
const int32_t displayId = mCursorController.getDisplayId();
const auto p = mCursorController.getPosition();
{
@@ -155,20 +168,28 @@
}
int32_t PointerController::getDisplayId() const {
+ if (!mEnabled) return ADISPLAY_ID_NONE;
+
return mCursorController.getDisplayId();
}
void PointerController::fade(Transition transition) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.fade(transition);
}
void PointerController::unfade(Transition transition) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.unfade(transition);
}
void PointerController::setPresentation(Presentation presentation) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
if (mLocked.presentation == presentation) {
@@ -193,6 +214,8 @@
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
const ui::Transform& transform = getTransformForDisplayLocked(displayId);
@@ -216,6 +239,8 @@
}
void PointerController::clearSpots() {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
clearSpotsLocked();
}
@@ -277,11 +302,15 @@
}
void PointerController::updatePointerIcon(PointerIconStyle iconId) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.updatePointerIcon(iconId);
}
void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.setCustomPointerIcon(icon);
}
@@ -326,6 +355,11 @@
}
void PointerController::dump(std::string& dump) {
+ if (!mEnabled) {
+ dump += INDENT "PointerController: DISABLED due to ongoing PointerChoreographer refactor\n";
+ return;
+ }
+
dump += INDENT "PointerController:\n";
std::scoped_lock lock(getLock());
dump += StringPrintf(INDENT2 "Presentation: %s\n",
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 01748a8..de39eda 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@
public:
static std::shared_ptr<PointerController> create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController);
+ SpriteController& spriteController, bool enabled);
~PointerController() override;
@@ -83,12 +83,13 @@
// Constructor used to test WindowInfosListener registration.
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController, WindowListenerConsumer registerListener,
+ SpriteController& spriteController, bool enabled,
+ WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener);
private:
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController);
+ SpriteController& spriteController, bool enabled);
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -99,6 +100,8 @@
// we use the DisplayInfoListener's lock in PointerController.
std::mutex& getLock() const;
+ const bool mEnabled;
+
PointerControllerContext mContext;
MouseCursorController mCursorController;
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 3e2e43f..94faf4a 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -181,7 +181,8 @@
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = PointerController::create(mPolicy, mLooper, *mSpriteController);
+ mPointerController =
+ PointerController::create(mPolicy, mLooper, *mSpriteController, /*enabled=*/true);
}
PointerControllerTest::~PointerControllerTest() {
@@ -321,6 +322,7 @@
const sp<Looper>& looper, SpriteController& spriteController)
: PointerController(
new MockPointerControllerPolicyInterface(), looper, spriteController,
+ /*enabled=*/true,
[®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
// Register listener
registeredListener = listener;
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index b50514d..283445f 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -36,7 +36,7 @@
void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am));
- ResTable_config config = locked_mgr->GetConfiguration();
+ ResTable_config config = locked_mgr->GetConfigurations()[0];
// AConfiguration is not a virtual subclass, so we can memcpy.
memcpy(out, &config, sizeof(config));
diff --git a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java
index 5c48c54..6b855c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java
+++ b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java
@@ -31,6 +31,7 @@
import java.util.Map;
public final class QrCodeGenerator {
+ private static final int DEFAULT_MARGIN = -1;
/**
* Generates a barcode image with {@code contents}.
*
@@ -40,7 +41,20 @@
*/
public static Bitmap encodeQrCode(String contents, int size)
throws WriterException, IllegalArgumentException {
- return encodeQrCode(contents, size, /*invert=*/false);
+ return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/false);
+ }
+
+ /**
+ * Generates a barcode image with {@code contents}.
+ *
+ * @param contents The contents to encode in the barcode
+ * @param size The preferred image size in pixels
+ * @param margin The margin around the actual barcode
+ * @return Barcode bitmap
+ */
+ public static Bitmap encodeQrCode(String contents, int size, int margin)
+ throws WriterException, IllegalArgumentException {
+ return encodeQrCode(contents, size, margin, /*invert=*/false);
}
/**
@@ -53,10 +67,27 @@
*/
public static Bitmap encodeQrCode(String contents, int size, boolean invert)
throws WriterException, IllegalArgumentException {
+ return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/invert);
+ }
+
+ /**
+ * Generates a barcode image with {@code contents}.
+ *
+ * @param contents The contents to encode in the barcode
+ * @param size The preferred image size in pixels
+ * @param margin The margin around the actual barcode
+ * @param invert Whether to invert the black/white pixels (e.g. for dark mode)
+ * @return Barcode bitmap
+ */
+ public static Bitmap encodeQrCode(String contents, int size, int margin, boolean invert)
+ throws WriterException, IllegalArgumentException {
final Map<EncodeHintType, Object> hints = new HashMap<>();
if (!isIso88591(contents)) {
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
}
+ if (margin != DEFAULT_MARGIN) {
+ hints.put(EncodeHintType.MARGIN, margin);
+ }
final BitMatrix qrBits = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,
size, size, hints);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2d62e2a..8787c25 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -217,6 +217,7 @@
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4494765..dfc3cef 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -312,6 +312,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d2b444b..7186aba 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1842,6 +1842,10 @@
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED);
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index 25269dc..44c4105 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -78,6 +78,7 @@
modifier = modifier,
enabled = enabled,
content = content,
+ colors = textButtonColors(),
)
}
@@ -85,26 +86,29 @@
@Composable
private fun filledButtonColors(): ButtonColors {
- val colors = LocalAndroidColorScheme.current.deprecated
+ val colors = LocalAndroidColorScheme.current
return ButtonDefaults.buttonColors(
- containerColor = colors.colorAccentPrimary,
- contentColor = colors.textColorOnAccent,
+ containerColor = colors.primary,
+ contentColor = colors.onPrimary,
)
}
@Composable
private fun outlineButtonColors(): ButtonColors {
- val colors = LocalAndroidColorScheme.current.deprecated
return ButtonDefaults.outlinedButtonColors(
- contentColor = colors.textColorPrimary,
+ contentColor = LocalAndroidColorScheme.current.onSurface,
)
}
@Composable
private fun outlineButtonBorder(): BorderStroke {
- val colors = LocalAndroidColorScheme.current.deprecated
return BorderStroke(
width = 1.dp,
- color = colors.colorAccentPrimaryVariant,
+ color = LocalAndroidColorScheme.current.primary,
)
}
+
+@Composable
+private fun textButtonColors(): ButtonColors {
+ return ButtonDefaults.textButtonColors(contentColor = LocalAndroidColorScheme.current.primary)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
new file mode 100644
index 0000000..48f40e7
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dialog.ui.composable
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.android.compose.theme.LocalAndroidColorScheme
+
+/**
+ * The content of an AlertDialog which can be used together with
+ * [SystemUIDialogFactory.create][com.android.systemui.statusbar.phone.create] to create an alert
+ * dialog in Compose.
+ *
+ * @see com.android.systemui.statusbar.phone.create
+ */
+@Composable
+fun AlertDialogContent(
+ title: @Composable () -> Unit,
+ content: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ icon: (@Composable () -> Unit)? = null,
+ positiveButton: (@Composable () -> Unit)? = null,
+ negativeButton: (@Composable () -> Unit)? = null,
+ neutralButton: (@Composable () -> Unit)? = null,
+) {
+ Column(
+ modifier.fillMaxWidth().verticalScroll(rememberScrollState()).padding(DialogPaddings),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ // Icon.
+ if (icon != null) {
+ val defaultSize = 32.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = defaultSize, minHeight = defaultSize),
+ propagateMinConstraints = true,
+ ) {
+ val iconColor = LocalAndroidColorScheme.current.primary
+ CompositionLocalProvider(LocalContentColor provides iconColor) { icon() }
+ }
+
+ Spacer(Modifier.height(16.dp))
+ }
+
+ // Title.
+ val titleColor = LocalAndroidColorScheme.current.onSurface
+ CompositionLocalProvider(LocalContentColor provides titleColor) {
+ ProvideTextStyle(
+ MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
+ ) {
+ title()
+ }
+ }
+ Spacer(Modifier.height(16.dp))
+
+ // Content.
+ val contentColor = LocalAndroidColorScheme.current.onSurfaceVariant
+ Box(Modifier.defaultMinSize(minHeight = 48.dp)) {
+ CompositionLocalProvider(LocalContentColor provides contentColor) {
+ ProvideTextStyle(
+ MaterialTheme.typography.bodyMedium.copy(textAlign = TextAlign.Center)
+ ) {
+ content()
+ }
+ }
+ }
+ Spacer(Modifier.height(32.dp))
+
+ // Buttons.
+ // TODO(b/283817398): If there is not enough space, the buttons should automatically stack
+ // as shown in go/sysui-dialog-styling.
+ if (positiveButton != null || negativeButton != null || neutralButton != null) {
+ Row(Modifier.fillMaxWidth()) {
+ if (neutralButton != null) {
+ neutralButton()
+ Spacer(Modifier.width(8.dp))
+ }
+
+ Spacer(Modifier.weight(1f))
+
+ if (negativeButton != null) {
+ negativeButton()
+ }
+
+ if (positiveButton != null) {
+ if (negativeButton != null) {
+ Spacer(Modifier.width(8.dp))
+ }
+
+ positiveButton()
+ }
+ }
+ }
+ }
+}
+
+private val DialogPaddings = PaddingValues(start = 24.dp, end = 24.dp, top = 24.dp, bottom = 18.dp)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index c865070..ffb20d8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -22,7 +22,9 @@
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.motionEventSpy
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
@@ -56,6 +58,7 @@
* must have entries in this map.
* @param modifier A modifier.
*/
+@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun SceneContainer(
viewModel: SceneContainerViewModel,
@@ -79,7 +82,7 @@
onChangeScene = viewModel::onSceneChanged,
transitions = SceneContainerTransitions,
state = state,
- modifier = modifier.fillMaxSize(),
+ modifier = modifier.fillMaxSize().motionEventSpy { viewModel.onUserInput() },
) {
sceneByKey.forEach { (sceneKey, composableScene) ->
scene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
new file mode 100644
index 0000000..5d6dd3b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.ComposeView
+import com.android.compose.theme.PlatformTheme
+
+/**
+ * Create a [SystemUIDialog] with the given [content].
+ *
+ * Note that the returned dialog will already have a background so the content should not draw an
+ * additional background.
+ *
+ * Example:
+ * ```
+ * val dialog = systemUiDialogFactory.create {
+ * AlertDialogContent(
+ * title = { Text("My title") },
+ * content = { Text("My content") },
+ * )
+ * }
+ *
+ * dialogLaunchAnimator.showFromView(dialog, viewThatWasClicked)
+ * ```
+ *
+ * @param context the [Context] in which the dialog will be constructed.
+ * @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the device
+ * is locked (true by default).
+ */
+fun SystemUIDialogFactory.create(
+ context: Context = this.applicationContext,
+ dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ content: @Composable (SystemUIDialog) -> Unit,
+): ComponentSystemUIDialog {
+ val dialog = create(context, dismissOnDeviceLock)
+
+ // Create the dialog so that it is properly constructed before we set the Compose content.
+ // Otherwise, the ComposeView won't render properly.
+ dialog.create()
+
+ // Set the content. Note that the background of the dialog is drawn on the DecorView of the
+ // dialog directly, which makes it automatically work nicely with DialogLaunchAnimator.
+ dialog.setContentView(
+ ComposeView(context).apply {
+ setContent {
+ PlatformTheme {
+ val defaultContentColor = MaterialTheme.colorScheme.onSurfaceVariant
+ CompositionLocalProvider(LocalContentColor provides defaultContentColor) {
+ content(dialog)
+ }
+ }
+ }
+ }
+ )
+
+ return dialog
+}
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 909048e..b00908f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -47,7 +47,7 @@
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="16dp"
- app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -65,7 +65,6 @@
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
- app:layout_constraintVertical_bias="1.0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/manage_text"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2dc1b45..62c4424 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -857,6 +857,8 @@
<dimen name="keyguard_security_container_padding_top">20dp</dimen>
+ <dimen name="keyguard_translate_distance_on_swipe_up">-200dp</dimen>
+
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
<dimen name="lock_icon_margin_bottom">74dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 04eae64..579358f 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -212,6 +212,8 @@
<item type="id" name="keyguard_indication_text" />
<item type="id" name="keyguard_indication_text_bottom" />
<item type="id" name="nssl_guideline" />
+ <item type="id" name="nssl_top_barrier" />
+ <item type="id" name="nssl_bottom_barrier" />
<item type="id" name="split_shade_guideline" />
<item type="id" name="lock_icon" />
<item type="id" name="lock_icon_bg" />
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index b23e085..a368703 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -586,16 +586,18 @@
if (shouldTryToDismissKeyguard()) {
tryDismissingKeyguard();
}
- onFingerDown(requestId,
- data.getPointerId(),
- data.getX(),
- data.getY(),
- data.getMinor(),
- data.getMajor(),
- data.getOrientation(),
- data.getTime(),
- data.getGestureStart(),
- mStatusBarStateController.isDozing());
+ if (!mOnFingerDown) {
+ onFingerDown(requestId,
+ data.getPointerId(),
+ data.getX(),
+ data.getY(),
+ data.getMinor(),
+ data.getMajor(),
+ data.getOrientation(),
+ data.getTime(),
+ data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+ }
// Pilfer if valid overlap, don't allow following events to reach keyguard
shouldPilfer = true;
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
new file mode 100644
index 0000000..b2bc06f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.shared.model
+
+/** Positioning info for the shared notification container */
+data class SharedNotificationContainerPosition(
+ val top: Float = 0f,
+ val bottom: Float = 0f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index c0d1951..b8de8d8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -43,6 +43,9 @@
val scaleForResolution: Flow<Float>
fun getResolutionScale(): Float
+
+ /** Convience to context.resources.getDimensionPixelSize() */
+ fun getDimensionPixelSize(id: Int): Int
}
@ExperimentalCoroutinesApi
@@ -115,4 +118,8 @@
}
return 1f
}
+
+ override fun getDimensionPixelSize(id: Int): Int {
+ return context.resources.getDimensionPixelSize(id)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index e9a539f..6b578ba 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -370,6 +370,9 @@
// 600- status bar
+ // TODO(b/291315866): Tracking Bug
+ @JvmField val SIGNAL_CALLBACK_DEPRECATION = unreleasedFlag("signal_callback_deprecation")
+
// TODO(b/265892345): Tracking Bug
val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip")
@@ -654,6 +657,10 @@
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
+ @JvmField
+ val COROUTINE_TRACING =
+ unreleasedFlag("coroutine_tracing")
+
// TODO(b/283071711): Tracking bug
@JvmField
val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 9b323ee..1cd8795 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -51,6 +51,7 @@
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
@@ -90,6 +91,7 @@
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
private val communalWidgetViewModel: CommunalWidgetViewModel,
private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
+ private val notificationStackScrollerLayoutController: NotificationStackScrollLayoutController,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -131,6 +133,7 @@
SharedNotificationContainerBinder.bind(
sharedNotificationContainer,
sharedNotificationContainerViewModel,
+ notificationStackScrollerLayoutController,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index ed84884..e13f675 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -19,10 +19,14 @@
import android.app.StatusBarManager
import android.graphics.Point
+import android.util.MathUtils
+import com.android.app.animation.Interpolators
+import com.android.systemui.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
@@ -38,12 +42,14 @@
import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -51,6 +57,7 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
@@ -66,7 +73,11 @@
featureFlags: FeatureFlags,
bouncerRepository: KeyguardBouncerRepository,
configurationRepository: ConfigurationRepository,
+ shadeRepository: ShadeRepository,
) {
+ /** Position information for the shared notification container. */
+ val sharedNotificationContainerPosition =
+ MutableStateFlow(SharedNotificationContainerPosition())
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
@@ -196,6 +207,22 @@
val keyguardAlpha: Flow<Float> = repository.keyguardAlpha
+ val keyguardTranslationY: Flow<Float> =
+ configurationChange.flatMapLatest {
+ val translationDistance =
+ configurationRepository.getDimensionPixelSize(
+ R.dimen.keyguard_translate_distance_on_swipe_up
+ )
+ shadeRepository.shadeModel.map {
+ // On swipe up, translate the keyguard to reveal the bouncer
+ MathUtils.lerp(
+ translationDistance,
+ 0,
+ Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount)
+ )
+ }
+ }
+
/** Whether to animate the next doze mode transition. */
val animateDozingTransitions: Flow<Boolean> = repository.animateBottomAreaDozingTransitions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 19f622b..2814732 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -18,6 +18,7 @@
import android.annotation.DrawableRes
import android.view.View
+import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -43,6 +44,9 @@
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@ExperimentalCoroutinesApi
object KeyguardRootViewBinder {
+
+ private var onLayoutChangeListener: OnLayoutChange? = null
+
@JvmStatic
fun bind(
view: ViewGroup,
@@ -54,8 +58,8 @@
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
- if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
launch {
occludingAppDeviceEntryMessageViewModel.message.collect {
biometricMessage ->
@@ -72,10 +76,8 @@
}
}
}
- }
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
launch {
viewModel.keyguardRootViewVisibilityState.collect { visibilityState ->
view.animate().cancel()
@@ -84,16 +86,21 @@
val isOcclusionTransitionRunning =
visibilityState.occlusionTransitionRunning
if (goingToFullShade) {
- view.animate().alpha(0f).setStartDelay(
- keyguardStateController.keyguardFadingAwayDelay
- ).setDuration(
- keyguardStateController.shortenedFadingAwayDuration
- ).setInterpolator(
- Interpolators.ALPHA_OUT
- ).withEndAction { view.visibility = View.GONE }.start()
+ view
+ .animate()
+ .alpha(0f)
+ .setStartDelay(
+ keyguardStateController.keyguardFadingAwayDelay
+ )
+ .setDuration(
+ keyguardStateController.shortenedFadingAwayDuration
+ )
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction { view.visibility = View.GONE }
+ .start()
} else if (
statusBarState == StatusBarState.KEYGUARD ||
- statusBarState == StatusBarState.SHADE_LOCKED
+ statusBarState == StatusBarState.SHADE_LOCKED
) {
view.visibility = View.VISIBLE
if (!isOcclusionTransitionRunning) {
@@ -105,15 +112,30 @@
}
}
+ launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
+ }
+
+ if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
launch {
- viewModel.alpha.collect { alpha ->
- view.alpha = alpha
+ viewModel.translationY.collect {
+ val statusView =
+ view.requireViewById<View>(R.id.keyguard_status_view)
+ statusView.translationY = it
}
}
}
}
}
- return disposableHandle
+
+ onLayoutChangeListener = OnLayoutChange(viewModel)
+ view.addOnLayoutChangeListener(onLayoutChangeListener)
+
+ return object : DisposableHandle {
+ override fun dispose() {
+ disposableHandle.dispose()
+ view.removeOnLayoutChangeListener(onLayoutChangeListener)
+ }
+ }
}
/**
@@ -122,10 +144,10 @@
private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
return ChipbarInfo(
startIcon =
- TintedIcon(
- Icon.Resource(icon, null),
- ChipbarInfo.DEFAULT_ICON_TINT,
- ),
+ TintedIcon(
+ Icon.Resource(icon, null),
+ ChipbarInfo.DEFAULT_ICON_TINT,
+ ),
text = Text.Loaded(message),
endItem = null,
vibrationEffect = null,
@@ -138,5 +160,31 @@
)
}
+ private class OnLayoutChange(private val viewModel: KeyguardRootViewModel) :
+ OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val ksv = v.findViewById(R.id.keyguard_status_view) as View?
+ val lockIcon = v.findViewById(R.id.lock_icon_view) as View?
+
+ if (ksv != null && lockIcon != null) {
+ // After layout, ensure the notifications are positioned correctly
+ viewModel.onSharedNotificationContainerPositionChanged(
+ ksv!!.top.toFloat() + ksv!!.height,
+ lockIcon!!.y
+ )
+ }
+ }
+ }
+
private const val ID = "occluding_app_device_entry_unlock_msg"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 3f319ba..f1f5973 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -18,20 +18,20 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.ViewGroup
-import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
-import javax.inject.Inject
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.END
+import com.android.systemui.R
+import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.util.Utils
+import javax.inject.Inject
-class DefaultStatusViewSection @Inject constructor(private val context: Context) :
- KeyguardSection {
+class DefaultStatusViewSection @Inject constructor(private val context: Context) : KeyguardSection {
private val statusViewId = R.id.keyguard_status_view
override fun apply(constraintSet: ConstraintSet) {
@@ -41,6 +41,15 @@
connect(statusViewId, TOP, PARENT_ID, TOP)
connect(statusViewId, START, PARENT_ID, START)
connect(statusViewId, END, PARENT_ID, END)
+
+ val margin =
+ if (LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)) {
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+ } else {
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
+ Utils.getStatusBarHeaderHeightKeyguard(context)
+ }
+ setMargin(statusViewId, TOP, margin)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 92b9ee4..c49af4d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import javax.inject.Inject
@@ -57,6 +58,8 @@
}
}
+ val translationY: Flow<Float> = keyguardInteractor.keyguardTranslationY
+
/**
* Puts this view-model in "preview mode", which means it's being used for UI that is rendering
* the lock screen preview in wallpaper picker / settings and not the real experience on the
@@ -66,4 +69,9 @@
val newPreviewMode = PreviewMode(true)
previewMode.value = newPreviewMode
}
+
+ fun onSharedNotificationContainerPositionChanged(top: Float, bottom: Float) {
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top, bottom)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
index 69cb611..b2a8719 100644
--- a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -39,6 +39,9 @@
/** Wakes up the device. */
fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int)
+
+ /** Notifies the power repository that a user touch happened. */
+ fun userTouch()
}
@SysUISingleton
@@ -83,6 +86,14 @@
)
}
+ override fun userTouch() {
+ manager.userActivity(
+ systemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH,
+ /* flags= */ 0,
+ )
+ }
+
companion object {
private const val TAG = "PowerRepository"
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 54376fe..e9a2428 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -18,6 +18,8 @@
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+import static com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION;
+
import android.annotation.NonNull;
import android.app.Dialog;
import android.content.Intent;
@@ -42,6 +44,7 @@
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -54,6 +57,8 @@
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -61,6 +66,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -79,6 +85,9 @@
private final NetworkController mNetworkController;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final Callback mCallback = new Callback();
+ private final WifiInteractor mWifiInteractor;
+ private final TileJavaAdapter mJavaAdapter;
+ private final FeatureFlags mFeatureFlags;
private boolean mWifiConnected;
private boolean mHotspotConnected;
@@ -97,7 +106,10 @@
KeyguardStateController keyguardStateController,
NetworkController networkController,
HotspotController hotspotController,
- DialogLaunchAnimator dialogLaunchAnimator
+ DialogLaunchAnimator dialogLaunchAnimator,
+ WifiInteractor wifiInteractor,
+ TileJavaAdapter javaAdapter,
+ FeatureFlags featureFlags
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -105,9 +117,16 @@
mKeyguard = keyguardStateController;
mNetworkController = networkController;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mWifiInteractor = wifiInteractor;
+ mJavaAdapter = javaAdapter;
+ mFeatureFlags = featureFlags;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
- mNetworkController.observe(this, mSignalCallback);
+ if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
+ mNetworkController.observe(this, mSignalCallback);
+ } else {
+ mJavaAdapter.bind(this, mWifiInteractor.getWifiNetwork(), mNetworkModelConsumer);
+ }
hotspotController.observe(this, mHotspotCallback);
}
@@ -293,19 +312,37 @@
return mWifiConnected || mHotspotConnected;
}
+ private void setWifiConnected(boolean connected) {
+ if (connected != mWifiConnected) {
+ mWifiConnected = connected;
+ // Hotspot is not connected, so changes here should update
+ if (!mHotspotConnected) {
+ refreshState();
+ }
+ }
+ }
+
+ private void setHotspotConnected(boolean connected) {
+ if (connected != mHotspotConnected) {
+ mHotspotConnected = connected;
+ // Wifi is not connected, so changes here should update
+ if (!mWifiConnected) {
+ refreshState();
+ }
+ }
+ }
+
+ private final Consumer<WifiNetworkModel> mNetworkModelConsumer = (model) -> {
+ setWifiConnected(model instanceof WifiNetworkModel.Active);
+ };
+
private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setWifiIndicators(@NonNull WifiIndicators indicators) {
// statusIcon.visible has the connected status information
boolean enabledAndConnected = indicators.enabled
- && (indicators.qsIcon == null ? false : indicators.qsIcon.visible);
- if (enabledAndConnected != mWifiConnected) {
- mWifiConnected = enabledAndConnected;
- // Hotspot is not connected, so changes here should update
- if (!mHotspotConnected) {
- refreshState();
- }
- }
+ && (indicators.qsIcon != null && indicators.qsIcon.visible);
+ setWifiConnected(enabledAndConnected);
}
};
@@ -314,13 +351,7 @@
@Override
public void onHotspotChanged(boolean enabled, int numDevices) {
boolean enabledAndConnected = enabled && numDevices > 0;
- if (enabledAndConnected != mHotspotConnected) {
- mHotspotConnected = enabledAndConnected;
- // Wifi is not connected, so changes here should update
- if (!mWifiConnected) {
- refreshState();
- }
- }
+ setHotspotConnected(enabledAndConnected);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
new file mode 100644
index 0000000..3b2f8b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.content.Context
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.view.View
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.AlphaControlledSignalTileView
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
+import javax.inject.Inject
+
+class InternetTileNewImpl
+@Inject
+constructor(
+ host: QSHost,
+ uiEventLogger: QsEventLogger,
+ @Background backgroundLooper: Looper,
+ @Main private val mainHandler: Handler,
+ falsingManager: FalsingManager,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ viewModel: InternetTileViewModel,
+ private val internetDialogFactory: InternetDialogFactory,
+ private val accessPointController: AccessPointController,
+) :
+ QSTileImpl<QSTile.SignalState>(
+ host,
+ uiEventLogger,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+ ) {
+ private var model: InternetTileModel = viewModel.tileModel.value
+
+ init {
+ InternetTileBinder.bind(lifecycle, viewModel.tileModel) { newModel ->
+ model = newModel
+ refreshState()
+ }
+ }
+
+ override fun createTileView(context: Context): QSIconView =
+ AlphaControlledSignalTileView(context)
+
+ override fun getTileLabel(): CharSequence =
+ mContext.getString(R.string.quick_settings_internet_label)
+
+ override fun newTileState(): QSTile.SignalState {
+ return QSTile.SignalState().also { it.forceExpandIcon = true }
+ }
+
+ override fun handleClick(view: View?) {
+ mainHandler.post {
+ internetDialogFactory.create(
+ aboveStatusBar = true,
+ accessPointController.canConfigMobileData(),
+ accessPointController.canConfigWifi(),
+ view,
+ )
+ }
+ }
+
+ override fun handleUpdateState(state: QSTile.SignalState, arg: Any?) {
+ state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
+
+ model.applyTo(state, mContext)
+ }
+
+ override fun getLongClickIntent(): Intent = WIFI_SETTINGS
+
+ companion object {
+ private val WIFI_SETTINGS = Intent(Settings.ACTION_WIFI_SETTINGS)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt
new file mode 100644
index 0000000..a2430ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.systemui.qs.tiles
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.SysUISingleton
+import java.util.function.Consumer
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/**
+ * Utility for binding tiles to kotlin flows. Similar to [JavaAdapter] and usable for QS tiles. We
+ * use [Lifecycle.State.RESUMED] here to match the implementation of [CallbackController.observe]
+ */
+@SysUISingleton
+class TileJavaAdapter @Inject constructor() {
+ fun <T> bind(
+ lifecycleOwner: LifecycleOwner,
+ flow: Flow<T>,
+ consumer: Consumer<T>,
+ ) {
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ flow.collect { consumer.accept(it) }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index a7434c6..342c218 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.power.data.repository.PowerRepository
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -44,6 +45,7 @@
constructor(
@Application applicationScope: CoroutineScope,
private val repository: SceneContainerRepository,
+ private val powerRepository: PowerRepository,
private val logger: SceneLogger,
) {
@@ -153,6 +155,11 @@
repository.setTransitionState(transitionState)
}
+ /** Handles a user input event. */
+ fun onUserInput() {
+ powerRepository.userTouch()
+ }
+
/**
* Notifies that the UI has transitioned sufficiently to the given scene.
*
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 5c16fb5..c5b605a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -63,6 +63,11 @@
interactor.setTransitionState(transitionState)
}
+ /** Handles an event representing user input. */
+ fun onUserInput() {
+ interactor.onUserInput()
+ }
+
companion object {
private const val SCENE_TRANSITION_LOGGING_REASON = "user input"
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 014093d..a41d6e8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -194,6 +194,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.BounceInterpolator;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -606,7 +607,7 @@
private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
-
+ private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final KeyguardViewConfigurator mKeyguardViewConfigurator;
@@ -772,6 +773,7 @@
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
+ SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
KeyguardRootView keyguardRootView) {
@@ -797,6 +799,7 @@
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
mKeyguardInteractor = keyguardInteractor;
mKeyguardViewConfigurator = keyguardViewConfigurator;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -1308,15 +1311,17 @@
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
- mNotificationStackScrollLayoutController.setOverExpansion(0);
- mNotificationStackScrollLayoutController.setOverScrollAmount(0);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mNotificationStackScrollLayoutController.setOverExpansion(0);
+ mNotificationStackScrollLayoutController.setOverScrollAmount(0);
+ }
// when we switch between split shade and regular shade we want to enforce setting qs to
// the default state: expanded for split shade and collapsed otherwise
- if (!isOnKeyguard() && mPanelExpanded) {
+ if (!isKeyguardShowing() && mPanelExpanded) {
mQsController.setExpanded(mSplitShadeEnabled);
}
- if (isOnKeyguard() && mQsController.getExpanded() && mSplitShadeEnabled) {
+ if (isKeyguardShowing() && mQsController.getExpanded() && mSplitShadeEnabled) {
// In single column keyguard - when you swipe from the top - QS is fully expanded and
// StatusBarState is KEYGUARD. That state doesn't make sense for split shade,
// where notifications are always visible and we effectively go to fully expanded
@@ -1327,7 +1332,9 @@
}
updateClockAppearance();
mQsController.updateQsState();
- mNotificationStackScrollLayoutController.updateFooter();
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mNotificationStackScrollLayoutController.updateFooter();
+ }
}
private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
@@ -1368,10 +1375,6 @@
attachSplitShadeMediaPlayerContainer(
keyguardStatusView.findViewById(R.id.status_view_media_container));
- } else {
- attachSplitShadeMediaPlayerContainer(
- mKeyguardViewConfigurator.getKeyguardRootView()
- .findViewById(R.id.status_view_media_container));
}
// we need to update KeyguardStatusView constraints after reinflating it
@@ -1400,6 +1403,12 @@
updateViewControllers(userAvatarView, keyguardUserSwitcherView);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ attachSplitShadeMediaPlayerContainer(
+ mKeyguardViewConfigurator.getKeyguardRootView()
+ .findViewById(R.id.status_view_media_container));
+ }
+
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1481,6 +1490,10 @@
if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
}
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return;
+ }
+
if (isKeyguardShowing() && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
@@ -1552,7 +1565,7 @@
private void positionClockAndNotifications(boolean forceClockUpdate) {
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
- boolean onKeyguard = isOnKeyguard();
+ boolean onKeyguard = isKeyguardShowing();
if (onKeyguard || forceClockUpdate) {
updateClockAppearance();
@@ -1624,8 +1637,10 @@
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
- mKeyguardStatusViewController.setLockscreenClockY(
- mClockPositionAlgorithm.getExpandedPreferredClockY());
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.setLockscreenClockY(
+ mClockPositionAlgorithm.getExpandedPreferredClockY());
+ }
if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
mKeyguardInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
@@ -1635,9 +1650,12 @@
}
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
- mKeyguardStatusViewController.updatePosition(
- mClockPositionResult.clockX, mClockPositionResult.clockY,
- mClockPositionResult.clockScale, animateClock);
+
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.updatePosition(
+ mClockPositionResult.clockX, mClockPositionResult.clockY,
+ mClockPositionResult.clockScale, animateClock);
+ }
if (mKeyguardQsUserSwitchController != null) {
mKeyguardQsUserSwitchController.updatePosition(
mClockPositionResult.clockX,
@@ -1867,8 +1885,12 @@
}
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
mKeyguardStatusViewController.setAlpha(alpha);
- mKeyguardStatusViewController
- .setTranslationY(mKeyguardOnlyTransitionTranslationY, /* excludeMedia= */true);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ // TODO (b/296373478) This is for split shade media movement.
+ } else {
+ mKeyguardStatusViewController
+ .setTranslationY(mKeyguardOnlyTransitionTranslationY, /* excludeMedia= */true);
+ }
if (mKeyguardQsUserSwitchController != null) {
mKeyguardQsUserSwitchController.setAlpha(alpha);
@@ -1980,7 +2002,7 @@
mQsController.setExpandImmediate(true);
setShowShelfOnly(true);
}
- if (mSplitShadeEnabled && isOnKeyguard()) {
+ if (mSplitShadeEnabled && isKeyguardShowing()) {
// It's a special case as this method is likely to not be initiated by finger movement
// but rather called from adb shell or accessibility service.
// We're using LockscreenShadeTransitionController because on lockscreen that's the
@@ -2024,7 +2046,7 @@
mQsController.setLastShadeFlingWasExpanding(expand);
mHeadsUpTouchHelper.notifyFling(!expand);
mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
- setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
+ setClosingWithAlphaFadeout(!expand && !isKeyguardShowing() && getFadeoutAlpha() == 1.0f);
mNotificationStackScrollLayoutController.setPanelFlinging(true);
if (target == mExpandedHeight && mOverExpansion == 0.0f) {
// We're at the target and didn't fling and there's no overshoot
@@ -2425,9 +2447,14 @@
}
void requestScrollerTopPaddingUpdate(boolean animate) {
- mNotificationStackScrollLayoutController.updateTopPadding(
- mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
- getKeyguardNotificationStaticPadding(), mExpandedFraction), animate);
+ float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
+ getKeyguardNotificationStaticPadding(), mExpandedFraction);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mSharedNotificationContainerInteractor.setTopPosition(padding);
+ } else {
+ mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
+ }
+
if (isKeyguardShowing()
&& mKeyguardBypassController.getBypassEnabled()) {
// update the position of the header
@@ -3022,7 +3049,7 @@
mNotificationStackScrollLayoutController
.setExpandingVelocity(getCurrentExpandVelocity());
}
- if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
+ if (mKeyguardBypassController.getBypassEnabled() && isKeyguardShowing()) {
// The expandedHeight is always the full panel Height when bypassing
expandedHeight = getMaxPanelHeight();
}
@@ -3033,7 +3060,7 @@
private void updateStatusBarIcons() {
boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
- if (showIconsWhenExpanded && isOnKeyguard()) {
+ if (showIconsWhenExpanded && isKeyguardShowing()) {
showIconsWhenExpanded = false;
}
if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
@@ -3047,10 +3074,6 @@
return mBarState;
}
- private boolean isOnKeyguard() {
- return mBarState == KEYGUARD;
- }
-
/** Called when a HUN is dragged up or down to indicate the starting height for shade motion. */
@VisibleForTesting
void setHeadsUpDraggingStartingHeight(int startHeight) {
@@ -4434,7 +4457,7 @@
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- if (!isOnKeyguard()) {
+ if (!isKeyguardShowing()) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
entry.getHeadsUpAnimationView(), true);
}
@@ -4447,7 +4470,7 @@
// we need to make sure that an animation happens in this case, otherwise the
// notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
+ if (isFullyCollapsed() && entry.isRowHeadsUp() && !isKeyguardShowing()) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
entry.getHeadsUpAnimationView(), false);
entry.setHeadsUpIsVisible();
@@ -4748,7 +4771,7 @@
// we need to ignore it on keyguard as this is a false alarm - transition from unlocked
// to locked will trigger this event and we're not actually in the process of opening
// the shade, lockscreen is just always expanded
- if (mSplitShadeEnabled && !isOnKeyguard()) {
+ if (mSplitShadeEnabled && !isKeyguardShowing()) {
mQsController.setExpandImmediate(true);
}
mOpenCloseListener.onOpenStarted();
@@ -4799,8 +4822,11 @@
private Consumer<Float> setTransitionY(
NotificationStackScrollLayoutController stackScroller) {
return (Float translationY) -> {
- mKeyguardStatusViewController.setTranslationY(translationY, /* excludeMedia= */false);
- stackScroller.setTranslationY(translationY);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.setTranslationY(translationY,
+ /* excludeMedia= */false);
+ stackScroller.setTranslationY(translationY);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index ebb9935..5a8be1e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -37,11 +37,18 @@
/** Amount qs has expanded. Quick Settings can be expanded without the full shade expansion. */
val qsExpansion: StateFlow<Float>
+ /** The amount the shade has expanded */
+ val shadeExpansion: StateFlow<Float>
+
/** Amount shade has expanded with regard to the UDFPS location */
val udfpsTransitionToFullShadeProgress: StateFlow<Float>
+ /** The amount QS has expanded without notifications */
fun setQsExpansion(qsExpansion: Float)
fun setUdfpsTransitionToFullShadeProgress(progress: Float)
+
+ /** The amount the shade has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded */
+ fun setShadeExpansion(expansion: Float)
}
/** Business logic for shade interactions */
@@ -69,7 +76,6 @@
val currentState = shadeExpansionStateManager.addExpansionListener(callback)
callback.onPanelExpansionChanged(currentState)
- trySendWithFailureLogging(ShadeModel(), TAG, "initial shade expansion info")
awaitClose { shadeExpansionStateManager.removeExpansionListener(callback) }
}
@@ -78,6 +84,9 @@
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
+ private val _shadeExpansion = MutableStateFlow(0f)
+ override val shadeExpansion: StateFlow<Float> = _shadeExpansion.asStateFlow()
+
private var _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
_udfpsTransitionToFullShadeProgress.asStateFlow()
@@ -85,6 +94,10 @@
_qsExpansion.value = qsExpansion
}
+ override fun setShadeExpansion(expansion: Float) {
+ _shadeExpansion.value = expansion
+ }
+
override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
_udfpsTransitionToFullShadeProgress.value = progress
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 6fde84a..288d32e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -19,6 +19,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -43,6 +45,7 @@
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
userInteractor: UserInteractor,
+ repository: ShadeRepository,
) {
/** Emits true if the shade is currently allowed and false otherwise. */
val isShadeEnabled: StateFlow<Boolean> =
@@ -50,6 +53,25 @@
.map { it.isShadeEnabled() }
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+ /** The amount [0-1] that the shade has been opened */
+ val shadeExpansion: Flow<Float> =
+ combine(repository.shadeExpansion, keyguardRepository.statusBarState) {
+ shadeExpansion,
+ statusBarState ->
+ // This is required, as shadeExpansion gets reset to 0f even with the shade open
+ if (statusBarState == StatusBarState.SHADE_LOCKED) {
+ 1f
+ } else {
+ shadeExpansion
+ }
+ }
+
+ /**
+ * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
+ * report 0f.
+ */
+ val qsExpansion: StateFlow<Float> = repository.qsExpansion
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 672796a..25bb204 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -424,6 +424,7 @@
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
fractionToShade =
MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
+ shadeRepository.setShadeExpansion(fractionToShade)
nsslController.setTransitionToFullShadeAmount(fractionToShade)
qsTransitionController.dragDownAmount = value
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 92aa986..b46b525 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.connectivity
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AirplaneModeTile
import com.android.systemui.qs.tiles.BluetoothTile
@@ -23,21 +25,17 @@
import com.android.systemui.qs.tiles.DataSaverTile
import com.android.systemui.qs.tiles.HotspotTile
import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.InternetTileNewImpl
import com.android.systemui.qs.tiles.NfcTile
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@Module
interface ConnectivityModule {
- /** Inject InternetTile into tileMap in QSModule */
- @Binds
- @IntoMap
- @StringKey(InternetTile.TILE_SPEC)
- fun bindInternetTile(internetTile: InternetTile): QSTileImpl<*>
-
/** Inject BluetoothTile into tileMap in QSModule */
@Binds
@IntoMap
@@ -70,4 +68,21 @@
/** Inject NfcTile into tileMap in QSModule */
@Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>
+
+ companion object {
+ /** Inject InternetTile or InternetTileNewImpl into tileMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(InternetTile.TILE_SPEC)
+ fun bindInternetTile(
+ internetTile: InternetTile,
+ newInternetTile: InternetTileNewImpl,
+ featureFlags: FeatureFlags,
+ ): QSTileImpl<*> =
+ if (featureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
+ newInternetTile
+ } else {
+ internetTile
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 874450b..4ed31c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -20,19 +20,27 @@
import android.content.Context
import com.android.systemui.R
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/** Encapsulates business-logic specifically related to the shared notification stack container. */
+@SysUISingleton
class SharedNotificationContainerInteractor
@Inject
constructor(
configurationRepository: ConfigurationRepository,
private val context: Context,
) {
+
+ private val _topPosition = MutableStateFlow(0f)
+ val topPosition = _topPosition.asStateFlow()
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
configurationRepository.onAnyConfigurationChange
.onStart { emit(Unit) }
@@ -54,6 +62,11 @@
}
.distinctUntilChanged()
+ /** Top position (without translation) of the shared container. */
+ fun setTopPosition(top: Float) {
+ _topPosition.value = top
+ }
+
data class ConfigurationBasedDimensions(
val useSplitShade: Boolean,
val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index fb1d55d..2af7181 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -19,6 +19,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import kotlinx.coroutines.launch
@@ -30,9 +31,10 @@
fun bind(
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
+ controller: NotificationStackScrollLayoutController,
) {
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.configurationBasedDimensions.collect {
view.updateConstraints(
@@ -42,8 +44,29 @@
marginEnd = it.marginEnd,
marginBottom = it.marginBottom,
)
+
+ controller.setOverExpansion(0f)
+ controller.setOverScrollAmount(0)
+ controller.updateFooter()
}
}
+
+ launch {
+ viewModel.maxNotifications.collect {
+ controller.setMaxDisplayedNotifications(it)
+ }
+ }
+
+ launch {
+ viewModel.position.collect {
+ controller.updateTopPadding(
+ it.top,
+ controller.isAddOrRemoveAnimationPending()
+ )
+ }
+ }
+
+ launch { viewModel.translationY.collect { controller.setTranslationY(it) } }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index b2e5ac1..6f4adeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -17,18 +17,35 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
-/** View-model for the shared notification container */
+/** View-model for the shared notification container, used by both the shade and keyguard spaces */
class SharedNotificationContainerViewModel
@Inject
constructor(
interactor: SharedNotificationContainerInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
+ controller: NotificationStackScrollLayoutController,
+ shadeInteractor: ShadeInteractor,
) {
+ private val statesForConstrainedNotifications =
+ setOf(KeyguardState.LOCKSCREEN, KeyguardState.AOD, KeyguardState.DOZING)
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
interactor.configurationBasedDimensions
.map {
@@ -43,6 +60,91 @@
}
.distinctUntilChanged()
+ /** If the user is visually on one of the unoccluded lockscreen states. */
+ val isOnLockscreen: Flow<Boolean> =
+ keyguardTransitionInteractor.finishedKeyguardState
+ .map { statesForConstrainedNotifications.contains(it) }
+ .distinctUntilChanged()
+
+ /** Are we purely on the keyguard without the shade/qs? */
+ val isOnLockscreenWithoutShade: Flow<Boolean> =
+ combine(
+ isOnLockscreen,
+ // Shade with notifications
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ // Shade without notifications, quick settings only (pull down from very top on
+ // lockscreen)
+ shadeInteractor.qsExpansion.map { it > 0f },
+ ) { isKeyguard, isShadeVisible, qsExpansion ->
+ isKeyguard && !(isShadeVisible || qsExpansion)
+ }
+ .distinctUntilChanged()
+
+ /**
+ * The container occupies the entire screen, and must be positioned relative to other elements.
+ *
+ * On keyguard, this generally fits below the clock and above the lock icon, or in split shade,
+ * the top of the screen to the lock icon.
+ *
+ * When the shade is expanding, the position is controlled by... the shade.
+ */
+ val position: Flow<SharedNotificationContainerPosition> =
+ isOnLockscreenWithoutShade.flatMapLatest { onLockscreen ->
+ if (onLockscreen) {
+ combine(
+ keyguardInteractor.sharedNotificationContainerPosition,
+ configurationBasedDimensions
+ ) { position, config ->
+ if (config.useSplitShade) {
+ position.copy(top = 0f)
+ } else {
+ position
+ }
+ }
+ } else {
+ interactor.topPosition.map { top ->
+ keyguardInteractor.sharedNotificationContainerPosition.value.copy(top = top)
+ }
+ }
+ }
+
+ /**
+ * Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
+ * translated as the keyguard fades out.
+ */
+ val translationY: Flow<Float> =
+ combine(isOnLockscreen, keyguardInteractor.keyguardTranslationY) {
+ isOnLockscreen,
+ translationY ->
+ if (isOnLockscreen) {
+ translationY
+ } else {
+ 0f
+ }
+ }
+
+ /**
+ * When on keyguard, there is limited space to display notifications so calculate how many could
+ * be shown. Otherwise, there is no limit since the vertical space will be scrollable.
+ * TODO: b/296606746 - Need to rerun logic when notifs change
+ */
+ val maxNotifications: Flow<Int> =
+ combine(isOnLockscreen, shadeInteractor.shadeExpansion, position) {
+ onLockscreen,
+ shadeExpansion,
+ positionInfo ->
+ if (onLockscreen && shadeExpansion < 1f) {
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ controller.getView(),
+ positionInfo.bottom - positionInfo.top,
+ 0f, // Vertical space for shelf is already accounted for
+ controller.getShelfHeight().toFloat(),
+ )
+ } else {
+ -1 // No limit
+ }
+ }
+
data class ConfigurationBasedDimensions(
val marginStart: Int,
val marginTop: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
new file mode 100644
index 0000000..72a5cfe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.content.Intent
+import androidx.lifecycle.LifecycleRegistry
+import com.android.keyguard.AuthKeyguardMessageArea
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.navigationbar.NavigationBarView
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.qs.QSPanelController
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import java.io.PrintWriter
+
+/**
+ * Empty implementation of [CentralSurfaces] for variants only need to override portions of the
+ * interface.
+ *
+ * **Important**: Prefer binding an Optional<CentralSurfaces> to an empty optional instead of
+ * including this class.
+ */
+abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
+ override val lifecycle = LifecycleRegistry(this)
+ override fun updateIsKeyguard() = false
+ override fun updateIsKeyguard(forceStateChange: Boolean) = false
+ override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
+ override fun isLaunchingActivityOverLockscreen() = false
+ override fun onKeyguardViewManagerStatesUpdated() {}
+ override fun isPulsing() = false
+ override fun isOccluded() = false
+ override fun isDeviceInVrMode() = false
+ override fun getPresenter(): NotificationPresenter? = null
+ override fun onInputFocusTransfer(start: Boolean, cancel: Boolean, velocity: Float) {}
+ override fun getCommandQueuePanelsEnabled() = false
+ override fun getBiometricUnlockController(): BiometricUnlockController? = null
+ override fun showWirelessChargingAnimation(batteryLevel: Int) {}
+ override fun checkBarModes() {}
+ override fun updateBubblesVisibility() {}
+ override fun setInteracting(barWindow: Int, interacting: Boolean) {}
+ override fun dump(pwOriginal: PrintWriter, args: Array<String>) {}
+ override fun getDisplayWidth() = 0f
+ override fun getDisplayHeight() = 0f
+ override fun readyForKeyguardDone() {}
+ override fun showKeyguard() {}
+ override fun hideKeyguard() = false
+ override fun showKeyguardImpl() {}
+ override fun fadeKeyguardAfterLaunchTransition(
+ beforeFading: Runnable?,
+ endRunnable: Runnable?,
+ cancelRunnable: Runnable?,
+ ) {}
+ override fun startLaunchTransitionTimeout() {}
+ override fun hideKeyguardImpl(forceStateChange: Boolean) = false
+ override fun keyguardGoingAway() {}
+ override fun setKeyguardFadingAway(startTime: Long, delay: Long, fadeoutDuration: Long) {}
+ override fun finishKeyguardFadingAway() {}
+ override fun userActivity() {}
+ override fun endAffordanceLaunch() {}
+ override fun shouldKeyguardHideImmediately() = false
+ override fun showBouncerWithDimissAndCancelIfKeyguard(
+ performAction: OnDismissAction?,
+ cancelAction: Runnable?,
+ ) {}
+ override fun getNavigationBarView(): NavigationBarView? = null
+ override fun isOverviewEnabled() = false
+ override fun showPinningEnterExitToast(entering: Boolean) {}
+ override fun showPinningEscapeToast() {}
+ override fun setBouncerShowing(bouncerShowing: Boolean) {}
+ override fun getWakefulnessState() = 0
+ override fun isScreenFullyOff() = false
+ override fun showScreenPinningRequest(taskId: Int, allowCancel: Boolean) {}
+ override fun getEmergencyActionIntent(): Intent? = null
+ override fun isCameraAllowedByAdmin() = false
+ override fun isGoingToSleep() = false
+ override fun notifyBiometricAuthModeChanged() {}
+ override fun setTransitionToFullShadeProgress(transitionToFullShadeProgress: Float) {}
+ override fun setPrimaryBouncerHiddenFraction(expansion: Float) {}
+ override fun updateScrimController() {}
+ override fun isKeyguardShowing() = false
+ override fun shouldIgnoreTouch() = false
+ override fun isDeviceInteractive() = false
+ override fun awakenDreams() {}
+ override fun clearNotificationEffects() {}
+ override fun isBouncerShowing() = false
+ override fun isBouncerShowingScrimmed() = false
+ override fun isBouncerShowingOverDream() = false
+ override fun isKeyguardSecure() = false
+ override fun updateNotificationPanelTouchState() {}
+ override fun getRotation() = 0
+ override fun setBarStateForTest(state: Int) {}
+ override fun showTransientUnchecked() {}
+ override fun clearTransient() {}
+ override fun acquireGestureWakeLock(time: Long) {}
+ override fun setAppearance(appearance: Int) = false
+ override fun getBarMode() = 0
+ override fun resendMessage(msg: Int) {}
+ override fun resendMessage(msg: Any?) {}
+ override fun setLastCameraLaunchSource(source: Int) {}
+ override fun setLaunchCameraOnFinishedGoingToSleep(launch: Boolean) {}
+ override fun setLaunchCameraOnFinishedWaking(launch: Boolean) {}
+ override fun setLaunchEmergencyActionOnFinishedGoingToSleep(launch: Boolean) {}
+ override fun setLaunchEmergencyActionOnFinishedWaking(launch: Boolean) {}
+ override fun getQSPanelController(): QSPanelController? = null
+ override fun getDisplayDensity() = 0f
+ override fun extendDozePulse() {}
+ override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {}
+ override fun getAnimatorControllerFromNotification(
+ associatedView: ExpandableNotificationRow?,
+ ): ActivityLaunchAnimator.Controller? = null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 6a3ebe6..4aa8b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2857,7 +2857,9 @@
&& mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON
&& mFingerprintManager.get().isPowerbuttonFps()
- && mFingerprintManager.get().hasEnrolledFingerprints()
+ && mKeyguardUpdateMonitor
+ .getCachedIsUnlockWithFingerprintPossible(
+ mUserTracker.getUserId())
&& !touchToUnlockAnytime;
if (DEBUG_WAKEUP_DELAY) {
Log.d(TAG, "mShouldDelayWakeUpAnimation=" + mShouldDelayWakeUpAnimation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
new file mode 100644
index 0000000..38a6d39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.annotation.CallSuper
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.model.SysUiState
+
+/**
+ * A [SystemUIDialog] that implements [LifecycleOwner], [SavedStateRegistryOwner] and
+ * [OnBackPressedDispatcherOwner].
+ *
+ * This class was forked from [androidx.activity.ComponentDialog] and can be used to easily create
+ * SystemUI dialogs without the need to subclass [SystemUIDialog]. You should call
+ * [SystemUIDialogFactory.create] to easily instantiate this class.
+ *
+ * Important: [ComponentSystemUIDialog] should be created and shown on the main thread.
+ *
+ * @see SystemUIDialogFactory.create
+ */
+class ComponentSystemUIDialog(
+ context: Context,
+ theme: Int,
+ dismissOnDeviceLock: Boolean,
+ featureFlags: FeatureFlags,
+ dialogManager: SystemUIDialogManager,
+ sysUiState: SysUiState,
+ broadcastDispatcher: BroadcastDispatcher,
+ dialogLaunchAnimator: DialogLaunchAnimator,
+) :
+ SystemUIDialog(
+ context,
+ theme,
+ dismissOnDeviceLock,
+ featureFlags,
+ dialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator
+ ),
+ LifecycleOwner,
+ SavedStateRegistryOwner,
+ OnBackPressedDispatcherOwner {
+ private var _lifecycleRegistry: LifecycleRegistry? = null
+ private val lifecycleRegistry: LifecycleRegistry
+ get() = _lifecycleRegistry ?: LifecycleRegistry(this).also { _lifecycleRegistry = it }
+
+ private val savedStateRegistryController: SavedStateRegistryController =
+ SavedStateRegistryController.create(this)
+ override val savedStateRegistry: SavedStateRegistry
+ get() = savedStateRegistryController.savedStateRegistry
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+
+ override fun onSaveInstanceState(): Bundle {
+ val bundle = super.onSaveInstanceState()
+ savedStateRegistryController.performSave(bundle)
+ return bundle
+ }
+
+ @CallSuper
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ onBackPressedDispatcher.setOnBackInvokedDispatcher(onBackInvokedDispatcher)
+ savedStateRegistryController.performRestore(savedInstanceState)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ }
+
+ @CallSuper
+ override fun start() {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ }
+
+ @CallSuper
+ override fun stop() {
+ // This is the closest thing to onDestroy that a Dialog has
+ // TODO(b/296180426): Make SystemUIDialog.onStop() and onStart() open again (annotated with
+ // @CallSuper) and do this *before* calling super.onStop(), like AndroidX ComponentDialog
+ // does.
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ _lifecycleRegistry = null
+ }
+
+ @Suppress("DEPRECATION")
+ override val onBackPressedDispatcher = OnBackPressedDispatcher { super.onBackPressed() }
+
+ @CallSuper
+ override fun onBackPressed() {
+ onBackPressedDispatcher.onBackPressed()
+ }
+
+ override fun setContentView(layoutResID: Int) {
+ initializeViewTreeOwners()
+ super.setContentView(layoutResID)
+ }
+
+ override fun setContentView(view: View) {
+ initializeViewTreeOwners()
+ super.setContentView(view)
+ }
+
+ override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
+ initializeViewTreeOwners()
+ super.setContentView(view, params)
+ }
+
+ override fun addContentView(view: View, params: ViewGroup.LayoutParams?) {
+ initializeViewTreeOwners()
+ super.addContentView(view, params)
+ }
+
+ /**
+ * Sets the view tree owners before setting the content view so that the inflation process and
+ * attach listeners will see them already present.
+ */
+ @CallSuper
+ open fun initializeViewTreeOwners() {
+ window!!.decorView.setViewTreeLifecycleOwner(this)
+ window!!.decorView.setViewTreeOnBackPressedDispatcherOwner(this)
+ window!!.decorView.setViewTreeSavedStateRegistryOwner(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index baf94fc..69510ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -68,7 +68,7 @@
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
- private static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
+ public static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
private final Context mContext;
private final FeatureFlags mFeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
new file mode 100644
index 0000000..3b15065
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.model.SysUiState
+import com.android.systemui.util.Assert
+import javax.inject.Inject
+
+/** A factory to easily instantiate a [ComponentSystemUIDialog]. */
+class SystemUIDialogFactory
+@Inject
+constructor(
+ @Application val applicationContext: Context,
+ private val featureFlags: FeatureFlagsClassic,
+ private val dialogManager: SystemUIDialogManager,
+ private val sysUiState: SysUiState,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+) {
+ /**
+ * Create a new [ComponentSystemUIDialog].
+ *
+ * Important: This should be called on the main thread and the returned dialog should be shown
+ * on the main thread.
+ *
+ * @param context the [Context] in which the dialog will be constructed.
+ * @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the
+ * device is locked (true by default).
+ */
+ fun create(
+ context: Context = this.applicationContext,
+ dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ ): ComponentSystemUIDialog {
+ Assert.isMainThread()
+
+ return ComponentSystemUIDialog(
+ context,
+ SystemUIDialog.DEFAULT_THEME,
+ dismissOnDeviceLock,
+ featureFlags,
+ dialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
new file mode 100644
index 0000000..3709e4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.ethernet.domain
+
+import com.android.settingslib.AccessibilityContentDescriptions
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Currently we don't do much to interact with ethernet. We simply need a place to map between the
+ * connectivity state of a default ethernet connection, and an icon representing that connection.
+ */
+@SysUISingleton
+class EthernetInteractor
+@Inject
+constructor(
+ connectivityRepository: ConnectivityRepository,
+) {
+ /** Icon representing the current connectivity status of the ethernet connection */
+ val icon: Flow<Icon.Resource?> =
+ connectivityRepository.defaultConnections.map {
+ if (it.ethernet.isDefault) {
+ if (it.isValidated) {
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet_fully,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
+ )
+ )
+ } else {
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
+ )
+ )
+ }
+ } else {
+ null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 051e88f..02473f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -16,9 +16,11 @@
package com.android.systemui.statusbar.pipeline.mobile.data
+import android.content.Intent
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyDisplayInfo
+import android.telephony.TelephonyManager
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.dagger.SysUISingleton
@@ -162,6 +164,28 @@
fun logOnSubscriptionsChanged() {
buffer.log(TAG, LogLevel.INFO, {}, { "onSubscriptionsChanged" })
}
+
+ fun logServiceProvidersUpdatedBroadcast(intent: Intent) {
+ val showSpn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)
+ val spn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
+ val showPlmn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)
+ val plmn = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
+
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ bool1 = showSpn
+ str1 = spn
+ bool2 = showPlmn
+ str2 = plmn
+ },
+ {
+ "Intent: ACTION_SERVICE_PROVIDERS_UPDATED." +
+ " showSpn=$bool1 spn=$str1 showPlmn=$bool2 plmn=$str2"
+ }
+ )
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 1f1ac92..cd68621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.annotation.SuppressLint
+import android.content.BroadcastReceiver
+import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
import android.telephony.CellSignalStrengthCdma
import android.telephony.ServiceState
import android.telephony.SignalStrength
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyCallback
import android.telephony.TelephonyDisplayInfo
@@ -34,6 +38,7 @@
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
import com.android.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
@@ -81,6 +86,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
override val subId: Int,
+ private val context: Context,
subscriptionModel: StateFlow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
@@ -323,16 +329,35 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), telephonyManager.simCarrierId)
+ /** BroadcastDispatcher does not handle sticky broadcasts, so we can't use it here */
+ @SuppressLint("RegisterReceiverViaContext")
override val networkName: StateFlow<NetworkNameModel> =
- broadcastDispatcher
- .broadcastFlow(
- filter = IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED),
- map = { intent, _ -> intent },
- )
- .filter { intent ->
- intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) == subId
+ conflatedCallbackFlow {
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (
+ intent.getIntExtra(
+ EXTRA_SUBSCRIPTION_INDEX,
+ INVALID_SUBSCRIPTION_ID
+ ) == subId
+ ) {
+ logger.logServiceProvidersUpdatedBroadcast(intent)
+ trySend(
+ intent.toNetworkNameModel(networkNameSeparator)
+ ?: defaultNetworkName
+ )
+ }
+ }
+ }
+
+ context.registerReceiver(
+ receiver,
+ IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)
+ )
+
+ awaitClose { context.unregisterReceiver(receiver) }
}
- .map { intent -> intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName }
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled = run {
@@ -349,6 +374,7 @@
class Factory
@Inject
constructor(
+ private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
private val telephonyManager: TelephonyManager,
private val logger: MobileInputLogger,
@@ -366,6 +392,7 @@
): MobileConnectionRepository {
return MobileConnectionRepositoryImpl(
subId,
+ context,
subscriptionModel,
defaultNetworkName,
networkNameSeparator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 4cfde5b..4bf297c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.content.Context
-import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.settingslib.graph.SignalDrawable
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
import com.android.systemui.dagger.qualifiers.Application
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.DefaultIcon
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.OverriddenIcon
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,24 +61,17 @@
*/
val isDataConnected: StateFlow<Boolean>
- /** Only true if mobile is the default transport but is not validated, otherwise false */
- val isDefaultConnectionFailed: StateFlow<Boolean>
-
/** True if we consider this connection to be in service, i.e. can make calls */
val isInService: StateFlow<Boolean>
- // TODO(b/256839546): clarify naming of default vs active
- /** True if we want to consider the data connection enabled */
- val isDefaultDataEnabled: StateFlow<Boolean>
-
/** Observable for the data enabled state of this connection */
val isDataEnabled: StateFlow<Boolean>
/** True if the RAT icon should always be displayed and false otherwise. */
val alwaysShowDataRatIcon: StateFlow<Boolean>
- /** True if the CDMA level should be preferred over the primary level. */
- val alwaysUseCdmaLevel: StateFlow<Boolean>
+ /** Canonical representation of the current mobile signal strength as a triangle. */
+ val signalLevelIcon: StateFlow<SignalIconModel>
/** Observable for RAT type (network type) indicator */
val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
@@ -108,9 +102,6 @@
/** True if there is only one active subscription. */
val isSingleCarrier: StateFlow<Boolean>
- /** True if this line of service is emergency-only */
- val isEmergencyOnly: StateFlow<Boolean>
-
/**
* True if this connection is considered roaming. The roaming bit can come from [ServiceState],
* or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
@@ -118,12 +109,6 @@
*/
val isRoaming: StateFlow<Boolean>
- /** Int describing the connection strength. 0-4 OR 1-5. See [numberOfLevels] */
- val level: StateFlow<Int>
-
- /** Based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL], either 4 or 5 */
- val numberOfLevels: StateFlow<Int>
-
/** See [MobileIconsInteractor.isForceHidden]. */
val isForceHidden: Flow<Boolean>
@@ -141,12 +126,12 @@
@Application scope: CoroutineScope,
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
- override val alwaysUseCdmaLevel: StateFlow<Boolean>,
+ alwaysUseCdmaLevel: StateFlow<Boolean>,
override val isSingleCarrier: StateFlow<Boolean>,
override val mobileIsDefault: StateFlow<Boolean>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
- override val isDefaultConnectionFailed: StateFlow<Boolean>,
+ isDefaultConnectionFailed: StateFlow<Boolean>,
override val isForceHidden: Flow<Boolean>,
connectionRepository: MobileConnectionRepository,
private val context: Context,
@@ -170,8 +155,6 @@
.distinctUntilChanged()
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
-
override val networkName =
combine(connectionRepository.operatorAlphaShort, connectionRepository.networkName) {
operatorAlphaShort,
@@ -255,8 +238,6 @@
DefaultIcon(defaultMobileIconGroup.value),
)
- override val isEmergencyOnly = connectionRepository.isEmergencyOnly
-
override val isRoaming: StateFlow<Boolean> =
combine(
connectionRepository.carrierNetworkChangeActive,
@@ -274,7 +255,7 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val level: StateFlow<Int> =
+ private val level: StateFlow<Int> =
combine(
connectionRepository.isGsm,
connectionRepository.primaryLevel,
@@ -290,7 +271,7 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
- override val numberOfLevels: StateFlow<Int> =
+ private val numberOfLevels: StateFlow<Int> =
connectionRepository.numberOfLevels.stateIn(
scope,
SharingStarted.WhileSubscribed(),
@@ -305,4 +286,54 @@
override val isInService = connectionRepository.isInService
override val isAllowedDuringAirplaneMode = connectionRepository.isAllowedDuringAirplaneMode
+
+ /** Whether or not to show the error state of [SignalDrawable] */
+ private val showExclamationMark: StateFlow<Boolean> =
+ combine(
+ defaultSubscriptionHasDataEnabled,
+ isDefaultConnectionFailed,
+ isInService,
+ ) { isDefaultDataEnabled, isDefaultConnectionFailed, isInService ->
+ !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), true)
+
+ private val shownLevel: StateFlow<Int> =
+ combine(
+ level,
+ isInService,
+ ) { level, isInService ->
+ if (isInService) level else 0
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+
+ override val signalLevelIcon: StateFlow<SignalIconModel> = run {
+ val initial =
+ SignalIconModel(
+ level = shownLevel.value,
+ numberOfLevels = numberOfLevels.value,
+ showExclamationMark = showExclamationMark.value,
+ carrierNetworkChange = carrierNetworkChangeActive.value,
+ )
+ combine(
+ shownLevel,
+ numberOfLevels,
+ showExclamationMark,
+ carrierNetworkChangeActive,
+ ) { shownLevel, numberOfLevels, showExclamationMark, carrierNetworkChange ->
+ SignalIconModel(
+ shownLevel,
+ numberOfLevels,
+ showExclamationMark,
+ carrierNetworkChange,
+ )
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "icon",
+ initialValue = initial,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index d08808b..62150e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.util.CarrierConfigTracker
+import java.lang.ref.WeakReference
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -70,6 +71,12 @@
/** True if the active mobile data subscription has data enabled */
val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
+ /**
+ * Flow providing a reference to the Interactor for the active data subId. This represents the
+ * [MobileConnectionInteractor] responsible for the active data connection, if any.
+ */
+ val activeDataIconInteractor: StateFlow<MobileIconInteractor?>
+
/** True if the RAT icon should always be displayed and false otherwise. */
val alwaysShowDataRatIcon: StateFlow<Boolean>
@@ -96,9 +103,9 @@
/**
* Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
- * subId. Will throw if the ID is invalid
+ * subId.
*/
- fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor
+ fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor
}
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -116,6 +123,9 @@
private val context: Context,
) : MobileIconsInteractor {
+ // Weak reference lookup for created interactors
+ private val reuseCache = mutableMapOf<Int, WeakReference<MobileIconInteractor>>()
+
override val mobileIsDefault =
combine(
mobileConnectionsRepo.mobileIsDefault,
@@ -138,6 +148,17 @@
.flatMapLatest { it?.dataEnabled ?: flowOf(false) }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val activeDataIconInteractor: StateFlow<MobileIconInteractor?> =
+ mobileConnectionsRepo.activeMobileDataSubscriptionId
+ .mapLatest {
+ if (it != null) {
+ getMobileConnectionInteractorForSubId(it)
+ } else {
+ null
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
private val unfilteredSubscriptions: Flow<List<SubscriptionModel>> =
mobileConnectionsRepo.subscriptions
@@ -306,21 +327,25 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
/** Vends out new [MobileIconInteractor] for a particular subId */
- override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
+ override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
+ reuseCache[subId]?.get() ?: createMobileConnectionInteractorForSubId(subId)
+
+ private fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
MobileIconInteractorImpl(
- scope,
- activeDataConnectionHasDataEnabled,
- alwaysShowDataRatIcon,
- alwaysUseCdmaLevel,
- isSingleCarrier,
- mobileIsDefault,
- defaultMobileIconMapping,
- defaultMobileIconGroup,
- isDefaultConnectionFailed,
- isForceHidden,
- mobileConnectionsRepo.getRepoForSubId(subId),
- context,
- )
+ scope,
+ activeDataConnectionHasDataEnabled,
+ alwaysShowDataRatIcon,
+ alwaysUseCdmaLevel,
+ isSingleCarrier,
+ mobileIsDefault,
+ defaultMobileIconMapping,
+ defaultMobileIconGroup,
+ isDefaultConnectionFailed,
+ isForceHidden,
+ mobileConnectionsRepo.getRepoForSubId(subId),
+ context,
+ )
+ .also { reuseCache[subId] = WeakReference(it) }
companion object {
private const val LOGGING_PREFIX = "Intr"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/SignalIconModel.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/SignalIconModel.kt
index 6de3f85..e58f081 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/SignalIconModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.mobile.ui.model
+package com.android.systemui.statusbar.pipeline.mobile.domain.model
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.log.table.Diffable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
index cffc833..a1a5370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -22,8 +22,8 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 275cfc5..dfabeea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -17,14 +17,13 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
-import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.CoroutineScope
@@ -76,26 +75,6 @@
constants: ConnectivityConstants,
scope: CoroutineScope,
) : MobileIconViewModelCommon {
- /** Whether or not to show the error state of [SignalDrawable] */
- private val showExclamationMark: StateFlow<Boolean> =
- combine(
- iconInteractor.isDefaultDataEnabled,
- iconInteractor.isDefaultConnectionFailed,
- iconInteractor.isInService,
- ) { isDefaultDataEnabled, isDefaultConnectionFailed, isInService ->
- !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), true)
-
- private val shownLevel: StateFlow<Int> =
- combine(
- iconInteractor.level,
- iconInteractor.isInService,
- ) { level, isInService ->
- if (isInService) level else 0
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
-
override val isVisible: StateFlow<Boolean> =
if (!constants.hasDataCapabilities) {
flowOf(false)
@@ -123,40 +102,12 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val icon: Flow<SignalIconModel> = run {
- val initial =
- SignalIconModel(
- level = shownLevel.value,
- numberOfLevels = iconInteractor.numberOfLevels.value,
- showExclamationMark = showExclamationMark.value,
- carrierNetworkChange = iconInteractor.carrierNetworkChangeActive.value,
- )
- combine(
- shownLevel,
- iconInteractor.numberOfLevels,
- showExclamationMark,
- iconInteractor.carrierNetworkChangeActive,
- ) { shownLevel, numberOfLevels, showExclamationMark, carrierNetworkChange ->
- SignalIconModel(
- shownLevel,
- numberOfLevels,
- showExclamationMark,
- carrierNetworkChange,
- )
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- iconInteractor.tableLogBuffer,
- columnPrefix = "icon",
- initialValue = initial,
- )
- .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
- }
+ override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
override val contentDescription: Flow<ContentDescription> = run {
val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[0])
- shownLevel
- .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it]) }
+ iconInteractor.signalLevelIcon
+ .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it.level]) }
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 216afb9..a4ec3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -101,7 +101,7 @@
val common = commonViewModelForSub(subId)
return LocationBasedMobileViewModel.viewModelForLocation(
common,
- mobileIconInteractorForSub(subId),
+ interactor.getMobileConnectionInteractorForSubId(subId),
verboseLogger,
location,
scope,
@@ -112,7 +112,7 @@
return mobileIconSubIdCache[subId]
?: MobileIconViewModel(
subId,
- mobileIconInteractorForSub(subId),
+ interactor.getMobileConnectionInteractorForSubId(subId),
airplaneModeInteractor,
constants,
scope,
@@ -120,14 +120,6 @@
.also { mobileIconSubIdCache[subId] = it }
}
- @VisibleForTesting
- fun mobileIconInteractorForSub(subId: Int): MobileIconInteractor {
- return mobileIconInteractorSubIdCache[subId]
- ?: interactor.createMobileConnectionInteractorForSubId(subId).also {
- mobileIconInteractorSubIdCache[subId] = it
- }
- }
-
private fun invalidateCaches(subIds: List<Int>) {
val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt
new file mode 100644
index 0000000..189dc40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.coroutineScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
+import java.util.function.Consumer
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Binds an [InternetTileModel] flow to a consumer for the internet tile to apply to its qs state
+ */
+object InternetTileBinder {
+ fun bind(
+ lifecycle: Lifecycle,
+ tileModelFlow: StateFlow<InternetTileModel>,
+ consumer: Consumer<InternetTileModel>
+ ) {
+ lifecycle.coroutineScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ tileModelFlow.collect { consumer.accept(it) }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
new file mode 100644
index 0000000..327dd8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.model
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.service.quicksettings.Tile
+import com.android.settingslib.graph.SignalDrawable
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.Text.Companion.loadText
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileImpl
+
+/** Model describing the state that the QS Internet tile should be in. */
+sealed interface InternetTileModel {
+ val secondaryTitle: String?
+ val secondaryLabel: Text?
+ val iconId: Int?
+ val icon: QSTile.Icon?
+
+ fun applyTo(state: QSTile.SignalState, context: Context) {
+ if (secondaryLabel != null) {
+ state.secondaryLabel = secondaryLabel.loadText(context)
+ } else {
+ state.secondaryLabel = secondaryTitle
+ }
+
+ // inout indicators are unused
+ state.activityIn = false
+ state.activityOut = false
+
+ // To support both SignalDrawable and other icons, give priority to icons over IDs
+ if (icon != null) {
+ state.icon = icon
+ } else if (iconId != null) {
+ state.icon = QSTileImpl.ResourceIcon.get(iconId!!)
+ }
+
+ state.state =
+ if (this is Active) {
+ Tile.STATE_ACTIVE
+ } else {
+ Tile.STATE_INACTIVE
+ }
+ }
+
+ data class Active(
+ override val secondaryTitle: String? = null,
+ override val secondaryLabel: Text? = null,
+ override val iconId: Int? = null,
+ override val icon: QSTile.Icon? = null,
+ ) : InternetTileModel
+
+ data class Inactive(
+ override val secondaryTitle: String? = null,
+ override val secondaryLabel: Text? = null,
+ override val iconId: Int? = null,
+ override val icon: QSTile.Icon? = null,
+ ) : InternetTileModel
+}
+
+/**
+ * [QSTile.Icon]-compatible container class for us to marshal the compacted [SignalDrawable] state
+ * across to the internet tile.
+ */
+data class SignalIcon(val state: Int) : QSTile.Icon() {
+
+ override fun getDrawable(context: Context): Drawable {
+ val d = SignalDrawable(context)
+ d.setLevel(state)
+ return d
+ }
+
+ override fun toString(): String {
+ return String.format("SignalIcon[mState=0x%08x]", state)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
new file mode 100644
index 0000000..120ba4e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.SignalIcon
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model for the quick settings [InternetTile]. This model exposes mainly a single flow of
+ * InternetTileModel objects, so that updating the tile is as simple as collecting on this state
+ * flow and then calling [QSTileImpl.refreshState]
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class InternetTileViewModel
+@Inject
+constructor(
+ airplaneModeRepository: AirplaneModeRepository,
+ connectivityRepository: ConnectivityRepository,
+ ethernetInteractor: EthernetInteractor,
+ mobileIconsInteractor: MobileIconsInteractor,
+ wifiInteractor: WifiInteractor,
+ private val context: Context,
+ @Application scope: CoroutineScope,
+) {
+ // Three symmetrical Flows that can be switched upon based on the value of
+ // [DefaultConnectionModel]
+ private val wifiIconFlow: Flow<InternetTileModel> =
+ wifiInteractor.wifiNetwork.flatMapLatest {
+ val wifiIcon = WifiIcon.fromModel(it, context)
+ if (it is WifiNetworkModel.Active && wifiIcon is WifiIcon.Visible) {
+ flowOf(
+ InternetTileModel.Active(
+ secondaryTitle = removeDoubleQuotes(it.ssid),
+ icon = ResourceIcon.get(wifiIcon.icon.res)
+ )
+ )
+ } else {
+ notConnectedFlow
+ }
+ }
+
+ private val mobileDataContentName: Flow<CharSequence?> =
+ mobileIconsInteractor.activeDataIconInteractor.flatMapLatest {
+ if (it == null) {
+ flowOf(null)
+ } else {
+ combine(it.isRoaming, it.networkTypeIconGroup) { isRoaming, networkTypeIconGroup ->
+ val cd = loadString(networkTypeIconGroup.contentDescription)
+ if (isRoaming) {
+ val roaming = context.getString(R.string.data_connection_roaming)
+ if (cd != null) {
+ context.getString(R.string.mobile_data_text_format, roaming, cd)
+ } else {
+ roaming
+ }
+ } else {
+ cd
+ }
+ }
+ }
+ }
+
+ private val mobileIconFlow: Flow<InternetTileModel> =
+ mobileIconsInteractor.activeDataIconInteractor.flatMapLatest {
+ if (it == null) {
+ notConnectedFlow
+ } else {
+ combine(
+ it.networkName,
+ it.signalLevelIcon,
+ mobileDataContentName,
+ ) { networkNameModel, signalIcon, dataContentDescription ->
+ InternetTileModel.Active(
+ secondaryTitle =
+ mobileDataContentConcat(networkNameModel.name, dataContentDescription),
+ icon = SignalIcon(signalIcon.toSignalDrawableState()),
+ )
+ }
+ }
+ }
+
+ private fun mobileDataContentConcat(
+ networkName: String?,
+ dataContentDescription: CharSequence?
+ ): String {
+ if (dataContentDescription == null) {
+ return networkName ?: ""
+ }
+ if (networkName == null) {
+ return dataContentDescription.toString()
+ }
+
+ return context.getString(
+ R.string.mobile_carrier_text_format,
+ networkName,
+ dataContentDescription
+ )
+ }
+
+ private fun loadString(resId: Int): String? =
+ if (resId > 0) {
+ context.getString(resId)
+ } else {
+ null
+ }
+
+ private val ethernetIconFlow: Flow<InternetTileModel> =
+ ethernetInteractor.icon.flatMapLatest {
+ if (it == null) {
+ notConnectedFlow
+ } else {
+ flowOf(
+ InternetTileModel.Active(
+ secondaryTitle = it.contentDescription.toString(),
+ iconId = it.res
+ )
+ )
+ }
+ }
+
+ private val notConnectedFlow: StateFlow<InternetTileModel> =
+ combine(
+ wifiInteractor.areNetworksAvailable,
+ airplaneModeRepository.isAirplaneMode,
+ ) { networksAvailable, isAirplaneMode ->
+ when {
+ isAirplaneMode -> {
+ InternetTileModel.Inactive(
+ secondaryTitle = context.getString(R.string.status_bar_airplane),
+ icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable)
+ )
+ }
+ networksAvailable -> {
+ InternetTileModel.Inactive(
+ secondaryTitle =
+ context.getString(R.string.quick_settings_networks_available),
+ iconId = R.drawable.ic_qs_no_internet_available,
+ )
+ }
+ else -> {
+ NOT_CONNECTED_NETWORKS_UNAVAILABLE
+ }
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), NOT_CONNECTED_NETWORKS_UNAVAILABLE)
+
+ /**
+ * Strict ordering of which repo is sending its data to the internet tile. Swaps between each of
+ * the interim providers (wifi, mobile, ethernet, or not-connected)
+ */
+ private val activeModelProvider: Flow<InternetTileModel> =
+ connectivityRepository.defaultConnections.flatMapLatest {
+ when {
+ it.ethernet.isDefault -> ethernetIconFlow
+ it.mobile.isDefault || it.carrierMerged.isDefault -> mobileIconFlow
+ it.wifi.isDefault -> wifiIconFlow
+ else -> notConnectedFlow
+ }
+ }
+
+ /** Consumable flow describing the correct state for the InternetTile */
+ val tileModel: StateFlow<InternetTileModel> =
+ activeModelProvider.stateIn(scope, SharingStarted.WhileSubscribed(), notConnectedFlow.value)
+
+ companion object {
+ val NOT_CONNECTED_NETWORKS_UNAVAILABLE =
+ InternetTileModel.Inactive(
+ secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable),
+ iconId = R.drawable.ic_qs_no_internet_unavailable,
+ )
+
+ private fun removeDoubleQuotes(string: String?): String? {
+ if (string == null) return null
+ val length = string.length
+ return if (length > 1 && string[0] == '"' && string[length - 1] == '"') {
+ string.substring(1, length - 1)
+ } else string
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index b5b99a7..b22e09e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import kotlinx.coroutines.flow.StateFlow
/** Provides data related to the wifi state. */
@@ -45,6 +46,12 @@
val wifiActivity: StateFlow<DataActivityModel>
/**
+ * The list of known wifi networks, per [WifiManager.scanResults]. This list is passively
+ * updated and does not trigger a scan.
+ */
+ val wifiScanResults: StateFlow<List<WifiScanEntry>>
+
+ /**
* Returns true if the device is currently connected to a wifi network with a valid SSID and
* false otherwise.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index 80091ac..ca042e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -124,4 +125,9 @@
activeRepo
.flatMapLatest { it.wifiActivity }
.stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiActivity.value)
+
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ activeRepo
+ .flatMapLatest { it.wifiScanResults }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiScanResults.value)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index 4b19c3a..152d181 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -55,6 +56,10 @@
MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity
+ private val _wifiScanResults: MutableStateFlow<List<WifiScanEntry>> =
+ MutableStateFlow(emptyList())
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> = _wifiScanResults
+
fun startProcessingCommands() {
demoCommandJob =
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
index 36c46a9..cfdbe4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
@@ -21,6 +21,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryViaTrackerLibDagger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -49,6 +50,9 @@
override val wifiActivity: StateFlow<DataActivityModel> =
MutableStateFlow(ACTIVITY).asStateFlow()
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ MutableStateFlow<List<WifiScanEntry>>(emptyList()).asStateFlow()
+
companion object {
private val NETWORK = WifiNetworkModel.Unavailable
private val ACTIVITY = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt
index f1b98b3..67dd32f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt
@@ -16,15 +16,21 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
+import android.annotation.SuppressLint
+import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -64,6 +70,34 @@
)
}
+ /**
+ * Creates a flow that listens for new [ScanResult]s from [WifiManager]. Does not request a scan
+ */
+ fun createNetworkScanFlow(
+ wifiManager: WifiManager,
+ scope: CoroutineScope,
+ @Background dispatcher: CoroutineDispatcher,
+ inputLogger: () -> Unit,
+ ): StateFlow<List<WifiScanEntry>> {
+ return conflatedCallbackFlow {
+ val callback =
+ object : WifiManager.ScanResultsCallback() {
+ @SuppressLint("MissingPermission")
+ override fun onScanResultsAvailable() {
+ inputLogger.invoke()
+ trySend(wifiManager.scanResults.toModel())
+ }
+ }
+
+ wifiManager.registerScanResultsCallback(dispatcher.asExecutor(), callback)
+
+ awaitClose { wifiManager.unregisterScanResultsCallback(callback) }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, emptyList())
+ }
+
+ private fun List<ScanResult>.toModel(): List<WifiScanEntry> = map { WifiScanEntry(it.SSID) }
+
// TODO(b/292534484): This print should only be done in [MessagePrinter] part of the log buffer.
private fun prettyPrintActivity(activity: Int): String {
return when (activity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 7c7b58d..59ef884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -231,6 +232,14 @@
logger::logActivity,
)
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ WifiRepositoryHelper.createNetworkScanFlow(
+ wifiManager,
+ scope,
+ bgDispatcher,
+ logger::logScanResults
+ )
+
companion object {
// Start out with no known wifi network.
// Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
index d4f40dd..9b404f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
@@ -23,6 +23,7 @@
import androidx.lifecycle.LifecycleRegistry
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_STATE_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive.toHotspotDeviceType
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
@@ -51,6 +53,7 @@
import com.android.wifitrackerlib.WifiPickerTracker
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
@@ -73,6 +76,7 @@
featureFlags: FeatureFlags,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
+ @Background private val bgDispatcher: CoroutineDispatcher,
private val wifiPickerTrackerFactory: WifiPickerTrackerFactory,
private val wifiManager: WifiManager,
@WifiTrackerLibInputLog private val inputLogger: LogBuffer,
@@ -300,6 +304,14 @@
this::logActivity,
)
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ WifiRepositoryHelper.createNetworkScanFlow(
+ wifiManager,
+ scope,
+ bgDispatcher,
+ this::logScanResults,
+ )
+
private fun logOnWifiEntriesChanged(connectedEntry: WifiEntry?) {
inputLogger.log(
TAG,
@@ -322,6 +334,9 @@
inputLogger.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "onActivityChanged: $str1" })
}
+ private fun logScanResults() =
+ inputLogger.log(TAG, LogLevel.DEBUG, {}, { "onScanResultsAvailable" })
+
/**
* Data class storing all the information fetched from [WifiPickerTracker].
*
@@ -345,6 +360,7 @@
private val featureFlags: FeatureFlags,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
+ @Background private val bgDispatcher: CoroutineDispatcher,
private val wifiPickerTrackerFactory: WifiPickerTrackerFactory,
@WifiTrackerLibInputLog private val inputLogger: LogBuffer,
@WifiTrackerLibTableLog private val wifiTrackerLibTableLogBuffer: TableLogBuffer,
@@ -354,6 +370,7 @@
featureFlags,
scope,
mainExecutor,
+ bgDispatcher,
wifiPickerTrackerFactory,
wifiManager,
inputLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 1a41abf..110e339 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -17,15 +17,21 @@
package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* The business logic layer for the wifi icon.
@@ -54,6 +60,9 @@
/** True if we're configured to force-hide the wifi icon and false otherwise. */
val isForceHidden: Flow<Boolean>
+
+ /** True if there are networks available other than the currently-connected one */
+ val areNetworksAvailable: StateFlow<Boolean>
}
@SysUISingleton
@@ -62,6 +71,7 @@
constructor(
connectivityRepository: ConnectivityRepository,
wifiRepository: WifiRepository,
+ @Application scope: CoroutineScope,
) : WifiInteractor {
override val ssid: Flow<String?> =
@@ -91,4 +101,26 @@
override val isForceHidden: Flow<Boolean> =
connectivityRepository.forceHiddenSlots.map { it.contains(ConnectivitySlot.WIFI) }
+
+ override val areNetworksAvailable: StateFlow<Boolean> =
+ combine(
+ wifiNetwork,
+ wifiRepository.wifiScanResults,
+ ) { currentNetwork, scanResults ->
+ // We consider networks to be available if the scan results list contains networks
+ // other than the one that is currently connected
+ if (scanResults.isEmpty()) {
+ false
+ } else if (currentNetwork !is WifiNetworkModel.Active) {
+ true
+ } else {
+ anyNonMatchingNetworkExists(currentNetwork, scanResults)
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ private fun anyNonMatchingNetworkExists(
+ currentNetwork: WifiNetworkModel.Active,
+ availableNetworks: List<WifiScanEntry>
+ ): Boolean = availableNetworks.firstOrNull { it.ssid != currentNetwork.ssid } != null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
index f244376..b76bb51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
@@ -59,6 +59,8 @@
fun logActivity(activity: String) {
buffer.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "Activity: $str1" })
}
+
+ fun logScanResults() = buffer.log(TAG, LogLevel.DEBUG, {}, { "onScanResultsAvailable" })
}
private const val TAG = "WifiInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt
new file mode 100644
index 0000000..d4a5a0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.shared.model
+
+/**
+ * Represents a single entry in the scan results callback. Use the [ssid] field to check against
+ * other networks
+ */
+data class WifiScanEntry(val ssid: String)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index 094bcf9..8156500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -17,10 +17,18 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.model
import android.annotation.DrawableRes
+import android.content.Context
+import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.statusbar.connectivity.WifiIcons
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
/** Represents the various states of the wifi icon. */
sealed interface WifiIcon : Diffable<WifiIcon> {
@@ -34,7 +42,7 @@
* description.
*/
class Visible(
- @DrawableRes res: Int,
+ @DrawableRes val res: Int,
val contentDescription: ContentDescription.Loaded,
) : WifiIcon {
val icon = Icon.Resource(res, contentDescription)
@@ -51,6 +59,46 @@
override fun logFull(row: TableRowLogger) {
row.logChange(COL_ICON, toString())
}
+
+ companion object {
+ @StringRes
+ @VisibleForTesting
+ internal val NO_INTERNET = R.string.data_connection_no_internet
+
+ /** Mapping from a [WifiNetworkModel] to the appropriate [WifiIcon] */
+ fun fromModel(model: WifiNetworkModel, context: Context): WifiIcon =
+ when (model) {
+ is WifiNetworkModel.Unavailable -> Hidden
+ is WifiNetworkModel.Invalid -> Hidden
+ is WifiNetworkModel.CarrierMerged -> Hidden
+ is WifiNetworkModel.Inactive ->
+ Visible(
+ res = WifiIcons.WIFI_NO_NETWORK,
+ ContentDescription.Loaded(
+ "${context.getString(WIFI_NO_CONNECTION)},${context.getString(
+ NO_INTERNET
+ )}"
+ )
+ )
+ is WifiNetworkModel.Active -> {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[model.level])
+ when {
+ model.isValidated ->
+ Visible(
+ WifiIcons.WIFI_FULL_ICONS[model.level],
+ ContentDescription.Loaded(levelDesc),
+ )
+ else ->
+ Visible(
+ WifiIcons.WIFI_NO_INTERNET_ICONS[model.level],
+ ContentDescription.Loaded(
+ "$levelDesc,${context.getString(NO_INTERNET)}"
+ ),
+ )
+ }
+ }
+ }
+ }
}
private const val COL_ICON = "icon"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index d9c2144..27ac7b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -17,19 +17,10 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.content.Context
-import androidx.annotation.StringRes
-import androidx.annotation.VisibleForTesting
-import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
-import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-import com.android.systemui.R
-import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
-import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
-import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON
import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
@@ -77,35 +68,7 @@
) : WifiViewModelCommon {
/** Returns the icon to use based on the given network. */
private fun WifiNetworkModel.icon(): WifiIcon {
- return when (this) {
- is WifiNetworkModel.Unavailable -> WifiIcon.Hidden
- is WifiNetworkModel.Invalid -> WifiIcon.Hidden
- is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
- is WifiNetworkModel.Inactive ->
- WifiIcon.Visible(
- res = WIFI_NO_NETWORK,
- ContentDescription.Loaded(
- "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
- )
- )
- is WifiNetworkModel.Active -> {
- val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
- when {
- this.isValidated ->
- WifiIcon.Visible(
- WIFI_FULL_ICONS[this.level],
- ContentDescription.Loaded(levelDesc),
- )
- else ->
- WifiIcon.Visible(
- WIFI_NO_INTERNET_ICONS[this.level],
- ContentDescription.Loaded(
- "$levelDesc,${context.getString(NO_INTERNET)}"
- ),
- )
- }
- }
- }
+ return WifiIcon.fromModel(this, context)
}
override val wifiIcon: StateFlow<WifiIcon> =
@@ -186,10 +149,4 @@
airplaneModeViewModel.isAirplaneModeIconVisible
override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get()
-
- companion object {
- @StringRes
- @VisibleForTesting
- internal val NO_INTERNET = R.string.data_connection_no_internet
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
index 1212585..feef029 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
@@ -16,9 +16,15 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
import android.view.View;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.util.Compile;
/**
* A class of utility static methods for heads up notifications.
@@ -26,12 +32,18 @@
public final class HeadsUpUtil {
private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
+ private static final String LOG_TAG = "HeadsUpUtil";
+ private static final boolean LOG_DEBUG = Compile.IS_DEBUG && Log.isLoggable(LOG_TAG, Log.DEBUG);
+
/**
* Set the given view as clicked or not-clicked.
* @param view The view to be set the flag to.
* @param clicked True to set as clicked. False to not-clicked.
*/
public static void setNeedsHeadsUpDisappearAnimationAfterClick(View view, boolean clicked) {
+ if (LOG_DEBUG) {
+ logTagClickedNotificationChanged(view, clicked);
+ }
view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
}
@@ -44,4 +56,36 @@
Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
return clicked != null && clicked;
}
+
+ private static void logTagClickedNotificationChanged(@Nullable View view, boolean isClicked) {
+ if (view == null) {
+ return;
+ }
+
+ final boolean wasClicked = isClickedHeadsUpNotification(view);
+ if (isClicked == wasClicked) {
+ return;
+ }
+
+ Log.d(LOG_TAG, getViewKey(view) + ": TAG_CLICKED_NOTIFICATION set to " + isClicked);
+ }
+
+ private static @NonNull String getViewKey(@NonNull View view) {
+ if (!(view instanceof ExpandableNotificationRow)) {
+ return "(not a row)";
+ }
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ final NotificationEntry entry = row.getEntry();
+ if (entry == null) {
+ return "(null entry)";
+ }
+
+ final String key = entry.getKey();
+ if (key == null) {
+ return "(null key)";
+ }
+
+ return key;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index b67f280..c91313ad 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -22,13 +22,11 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.ClockAnimations
@@ -38,7 +36,6 @@
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockTickRate
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -47,6 +44,8 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
+import java.util.TimeZone
+import java.util.concurrent.Executor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
@@ -63,10 +62,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import java.util.TimeZone
-import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -82,7 +79,6 @@
@Mock private lateinit var clock: ClockController
@Mock private lateinit var mainExecutor: DelayableExecutor
@Mock private lateinit var bgExecutor: Executor
- @Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var smallClockController: ClockFaceController
@Mock private lateinit var smallClockView: View
@Mock private lateinit var smallClockViewTreeObserver: ViewTreeObserver
@@ -95,9 +91,7 @@
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
@Mock private lateinit var transitionRepository: KeyguardTransitionRepository
- @Mock private lateinit var commandQueue: CommandQueue
private lateinit var repository: FakeKeyguardRepository
- private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
@Mock private lateinit var smallLogBuffer: LogBuffer
@Mock private lateinit var largeLogBuffer: LogBuffer
private lateinit var underTest: ClockEventController
@@ -123,31 +117,35 @@
.thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
repository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
- underTest = ClockEventController(
- KeyguardInteractor(
+ val withDeps =
+ KeyguardInteractorFactory.create(
repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = bouncerRepository,
- configurationRepository = FakeConfigurationRepository(),
- ),
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- ).keyguardTransitionInteractor,
- broadcastDispatcher,
- batteryController,
- keyguardUpdateMonitor,
- configurationController,
- context.resources,
- context,
- mainExecutor,
- bgExecutor,
- smallLogBuffer,
- largeLogBuffer,
- featureFlags
- )
+ )
+
+ withDeps.featureFlags.apply {
+ set(Flags.REGION_SAMPLING, false)
+ set(Flags.DOZING_MIGRATION_1, false)
+ }
+ underTest =
+ ClockEventController(
+ withDeps.keyguardInteractor,
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
+ broadcastDispatcher,
+ batteryController,
+ keyguardUpdateMonitor,
+ configurationController,
+ context.resources,
+ context,
+ mainExecutor,
+ bgExecutor,
+ smallLogBuffer,
+ largeLogBuffer,
+ withDeps.featureFlags
+ )
underTest.clock = clock
runBlocking(IMMEDIATE) {
@@ -171,38 +169,41 @@
}
@Test
- fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
- verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
- verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
+ fun themeChanged_verifyClockPaletteUpdated() =
+ runBlocking(IMMEDIATE) {
+ verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
- val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
- verify(configurationController).addCallback(capture(captor))
- captor.value.onThemeChanged()
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
+ captor.value.onThemeChanged()
- verify(events).onColorPaletteChanged(any())
- }
+ verify(events).onColorPaletteChanged(any())
+ }
@Test
- fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
- verify(configurationController).addCallback(capture(captor))
- captor.value.onDensityOrFontScaleChanged()
+ fun fontChanged_verifyFontSizeUpdated() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
+ captor.value.onDensityOrFontScaleChanged()
- verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat())
- verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat())
- }
+ verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat())
+ verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat())
+ }
@Test
- fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) {
- val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
- verify(batteryController).addCallback(capture(batteryCaptor))
- val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
- keyguardCaptor.value.onKeyguardVisibilityChanged(true)
- batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+ fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() =
+ runBlocking(IMMEDIATE) {
+ val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+ verify(batteryController).addCallback(capture(batteryCaptor))
+ val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+ keyguardCaptor.value.onKeyguardVisibilityChanged(true)
+ batteryCaptor.value.onBatteryLevelChanged(10, false, true)
- verify(animations, times(2)).charge()
- }
+ verify(animations, times(2)).charge()
+ }
@Test
fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() =
@@ -219,16 +220,17 @@
}
@Test
- fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) {
- val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
- verify(batteryController).addCallback(capture(batteryCaptor))
- val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
- keyguardCaptor.value.onKeyguardVisibilityChanged(false)
- batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+ fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() =
+ runBlocking(IMMEDIATE) {
+ val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+ verify(batteryController).addCallback(capture(batteryCaptor))
+ val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+ keyguardCaptor.value.onKeyguardVisibilityChanged(false)
+ batteryCaptor.value.onBatteryLevelChanged(10, false, true)
- verify(animations, never()).charge()
- }
+ verify(animations, never()).charge()
+ }
@Test
fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() =
@@ -244,102 +246,111 @@
}
@Test
- fun localeCallback_verifyClockNotified() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<BroadcastReceiver>()
- verify(broadcastDispatcher).registerReceiver(
- capture(captor), any(), eq(null), eq(null), anyInt(), eq(null)
- )
- captor.value.onReceive(context, mock())
+ fun localeCallback_verifyClockNotified() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(broadcastDispatcher)
+ .registerReceiver(capture(captor), any(), eq(null), eq(null), anyInt(), eq(null))
+ captor.value.onReceive(context, mock())
- verify(events).onLocaleChanged(any())
- }
+ verify(events).onLocaleChanged(any())
+ }
@Test
- fun keyguardCallback_visibilityChanged_clockDozeCalled() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ fun keyguardCallback_visibilityChanged_clockDozeCalled() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onKeyguardVisibilityChanged(true)
- verify(animations, never()).doze(0f)
+ captor.value.onKeyguardVisibilityChanged(true)
+ verify(animations, never()).doze(0f)
- captor.value.onKeyguardVisibilityChanged(false)
- verify(animations, times(2)).doze(0f)
- }
+ captor.value.onKeyguardVisibilityChanged(false)
+ verify(animations, times(2)).doze(0f)
+ }
@Test
- fun keyguardCallback_timeFormat_clockNotified() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onTimeFormatChanged("12h")
+ fun keyguardCallback_timeFormat_clockNotified() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ captor.value.onTimeFormatChanged("12h")
- verify(events).onTimeFormatChanged(false)
- }
+ verify(events).onTimeFormatChanged(false)
+ }
@Test
- fun keyguardCallback_timezoneChanged_clockNotified() = runBlocking(IMMEDIATE) {
- val mockTimeZone = mock<TimeZone>()
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onTimeZoneChanged(mockTimeZone)
+ fun keyguardCallback_timezoneChanged_clockNotified() =
+ runBlocking(IMMEDIATE) {
+ val mockTimeZone = mock<TimeZone>()
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ captor.value.onTimeZoneChanged(mockTimeZone)
- verify(events).onTimeZoneChanged(mockTimeZone)
- }
+ verify(events).onTimeZoneChanged(mockTimeZone)
+ }
@Test
- fun keyguardCallback_userSwitched_clockNotified() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onUserSwitchComplete(10)
+ fun keyguardCallback_userSwitched_clockNotified() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ captor.value.onUserSwitchComplete(10)
- verify(events).onTimeFormatChanged(false)
- }
+ verify(events).onTimeFormatChanged(false)
+ }
@Test
- fun keyguardCallback_verifyKeyguardChanged() = runBlocking(IMMEDIATE) {
- val job = underTest.listenForDozeAmount(this)
- repository.setDozeAmount(0.4f)
+ fun keyguardCallback_verifyKeyguardChanged() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozeAmount(this)
+ repository.setDozeAmount(0.4f)
- yield()
+ yield()
- verify(animations, times(2)).doze(0.4f)
+ verify(animations, times(2)).doze(0.4f)
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun unregisterListeners_validate() = runBlocking(IMMEDIATE) {
- underTest.unregisterListeners()
- verify(broadcastDispatcher).unregisterReceiver(any())
- verify(configurationController).removeCallback(any())
- verify(batteryController).removeCallback(any())
- verify(keyguardUpdateMonitor).removeCallback(any())
- verify(smallClockController.view)
+ fun unregisterListeners_validate() =
+ runBlocking(IMMEDIATE) {
+ underTest.unregisterListeners()
+ verify(broadcastDispatcher).unregisterReceiver(any())
+ verify(configurationController).removeCallback(any())
+ verify(batteryController).removeCallback(any())
+ verify(keyguardUpdateMonitor).removeCallback(any())
+ verify(smallClockController.view)
.removeOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
- verify(largeClockController.view)
+ verify(largeClockController.view)
.removeOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
- }
+ }
@Test
- fun registerOnAttachStateChangeListener_validate() = runBlocking(IMMEDIATE) {
- verify(smallClockController.view)
- .addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
- verify(largeClockController.view)
- .addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
- }
+ fun registerOnAttachStateChangeListener_validate() =
+ runBlocking(IMMEDIATE) {
+ verify(smallClockController.view)
+ .addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
+ verify(largeClockController.view)
+ .addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
+ }
@Test
- fun registerAndRemoveOnGlobalLayoutListener_correctly() = runBlocking(IMMEDIATE) {
- underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
- verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
- underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow(smallClockView)
- verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
- }
+ fun registerAndRemoveOnGlobalLayoutListener_correctly() =
+ runBlocking(IMMEDIATE) {
+ underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
+ verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
+ underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow(
+ smallClockView
+ )
+ verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
+ }
@Test
fun registerOnGlobalLayoutListener_RemoveOnAttachStateChangeListener_correctly() =
runBlocking(IMMEDIATE) {
- underTest.smallClockOnAttachStateChangeListener!!
- .onViewAttachedToWindow(smallClockView)
+ underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
underTest.unregisterListeners()
verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index b18137c..b9e3f14 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -47,7 +47,6 @@
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
import com.android.systemui.plugins.FalsingManager;
@@ -92,7 +91,6 @@
protected @Mock ConfigurationController mConfigurationController;
protected @Mock VibratorHelper mVibrator;
protected @Mock AuthRippleController mAuthRippleController;
- protected @Mock KeyguardTransitionRepository mTransitionRepository;
protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
protected FakeFeatureFlags mFeatureFlags;
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@@ -165,8 +163,8 @@
mAuthRippleController,
mResources,
KeyguardTransitionInteractorFactory.create(
- TestScopeProvider.getTestScope().getBackgroundScope(),
- mTransitionRepository).getKeyguardTransitionInteractor(),
+ TestScopeProvider.getTestScope().getBackgroundScope())
+ .getKeyguardTransitionInteractor(),
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags,
mPrimaryBouncerInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 7ab8e8b..e56b5c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1616,4 +1616,43 @@
// THEN vibrate is used
verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
}
+
+ @Test
+ public void aodInterrupt_withNewTouchDetection() throws RemoteException {
+ mUdfpsController.cancelAodSendFingerUpAction();
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown =
+ new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+ 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // GIVEN that the overlay is showing and screen is on and fp is running
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, 0,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mScreenObserver.onScreenTurnedOn();
+ mFgExecutor.runAllReady();
+
+ // WHEN fingerprint is requested because of AOD interrupt
+ mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
+
+ // Check case where touch driver sends touch to UdfpsView as well
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+
+ mBiometricExecutor.runAllReady();
+
+ // THEN only one onPointerDown is sent
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index e73d580..36822e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -34,8 +34,6 @@
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -45,9 +43,8 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
@@ -58,7 +55,6 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
@@ -95,7 +91,6 @@
@Mock private lateinit var backgroundHandler: Handler
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@@ -183,13 +178,10 @@
underTest.interactor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(
- repository = FakeKeyguardRepository(),
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- configurationRepository = FakeConfigurationRepository(),
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .keyguardInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 4b09468..972af4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.test.TestScope
@@ -52,6 +53,7 @@
private lateinit var repository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var configurationRepository: FakeConfigurationRepository
+ private lateinit var shadeRepository: FakeShadeRepository
@Before
fun setUp() {
@@ -62,6 +64,7 @@
repository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
configurationRepository = FakeConfigurationRepository()
+ shadeRepository = FakeShadeRepository()
underTest =
KeyguardInteractor(
repository,
@@ -69,6 +72,7 @@
featureFlags,
bouncerRepository,
configurationRepository,
+ shadeRepository,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 1dcb55d..f24ea6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -116,13 +116,13 @@
keyguardUpdateMonitor,
),
fingerprintAuthRepository,
- KeyguardInteractor(
- keyguardRepository,
- commandQueue = mock(),
- featureFlags,
- bouncerRepository,
- configurationRepository,
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ repository = keyguardRepository,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ )
+ .keyguardInteractor,
PrimaryBouncerInteractor(
bouncerRepository,
primaryBouncerView = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
index a01394f..f566efe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -193,6 +193,23 @@
assertThat(reasonCaptor.value).contains(context.applicationContext.packageName)
}
+ @Test
+ fun userActivity_notifiesPowerManager() {
+ systemClock.setUptimeMillis(345000)
+
+ underTest.userTouch()
+
+ val flagsCaptor = argumentCaptor<Int>()
+ verify(manager)
+ .userActivity(
+ eq(345000L),
+ eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
+ capture(flagsCaptor)
+ )
+ assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS)
+ assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_INDIRECT)
+ }
+
private fun verifyRegistered() {
// We must verify with all arguments, even those that are optional because they have default
// values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 93ed994..e537131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION;
+
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
@@ -37,9 +39,11 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -49,6 +53,12 @@
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository;
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl;
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -65,6 +75,7 @@
import java.util.ArrayList;
import java.util.List;
+import kotlinx.coroutines.test.TestScope;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -98,6 +109,12 @@
@Mock
private QsEventLogger mUiEventLogger;
+ private WifiInteractor mWifiInteractor;
+ private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
+ private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -108,42 +125,11 @@
when(mHost.getContext()).thenReturn(mContext);
- mCastTile = new CastTile(
- mHost,
- mUiEventLogger,
- mTestableLooper.getLooper(),
- new Handler(mTestableLooper.getLooper()),
- new FalsingManagerFake(),
- mMetricsLogger,
- mStatusBarStateController,
- mActivityStarter,
- mQSLogger,
- mController,
- mKeyguard,
- mNetworkController,
- mHotspotController,
- mDialogLaunchAnimator
+ mWifiInteractor = new WifiInteractorImpl(
+ new FakeConnectivityRepository(),
+ mWifiRepository,
+ mTestScope
);
- mCastTile.initialize();
-
- // We are not setting the mocks to listening, so we trigger a first refresh state to
- // set the initial state
- mCastTile.refreshState();
-
- mTestableLooper.processAllMessages();
-
- mCastTile.handleSetListening(true);
- ArgumentCaptor<SignalCallback> signalCallbackArgumentCaptor =
- ArgumentCaptor.forClass(SignalCallback.class);
- verify(mNetworkController).observe(any(LifecycleOwner.class),
- signalCallbackArgumentCaptor.capture());
- mSignalCallback = signalCallbackArgumentCaptor.getValue();
-
- ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
- ArgumentCaptor.forClass(HotspotController.Callback.class);
- verify(mHotspotController).observe(any(LifecycleOwner.class),
- hotspotCallbackArgumentCaptor.capture());
- mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
}
@After
@@ -156,10 +142,11 @@
// All these tests for enabled/disabled wifi have hotspot not enabled
@Test
public void testStateUnavailable_wifiDisabled() {
+ createAndStartTileOldImpl();
IconState qsIcon = new IconState(false, 0, "");
WifiIndicators indicators = new WifiIndicators(
false, mock(IconState.class),
- qsIcon, false,false, "",
+ qsIcon, false, false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -169,10 +156,11 @@
@Test
public void testStateUnavailable_wifiNotConnected() {
+ createAndStartTileOldImpl();
IconState qsIcon = new IconState(false, 0, "");
WifiIndicators indicators = new WifiIndicators(
true, mock(IconState.class),
- qsIcon, false,false, "",
+ qsIcon, false, false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -184,7 +172,7 @@
IconState qsIcon = new IconState(true, 0, "");
WifiIndicators indicators = new WifiIndicators(
true, mock(IconState.class),
- qsIcon, false,false, "",
+ qsIcon, false, false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -192,6 +180,7 @@
@Test
public void testStateActive_wifiEnabledAndCasting() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastController.CastDevice.STATE_CONNECTED;
List<CastDevice> devices = new ArrayList<>();
@@ -204,15 +193,87 @@
@Test
public void testStateInactive_wifiEnabledNotCasting() {
+ createAndStartTileOldImpl();
enableWifiAndProcessMessages();
assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
}
// -------------------------------------------------
// -------------------------------------------------
+ // All these tests for enabled/disabled wifi have hotspot not enabled, and have the
+ // SIGNAL_CALLBACK_DEPRECATION flag set to true
+
+ @Test
+ public void stateUnavailable_wifiDisabled_newPipeline() {
+ createAndStartTileNewImpl();
+ mWifiRepository.setIsWifiEnabled(false);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateUnavailable_wifiEnabled_notConnected_newPipeline() {
+ createAndStartTileNewImpl();
+ mWifiRepository.setIsWifiEnabled(true);
+ mWifiRepository.setWifiNetwork(Inactive.INSTANCE);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateActive_wifiConnectedAndCasting_newPipeline() {
+ createAndStartTileNewImpl();
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ mWifiRepository.setWifiNetwork(
+ new WifiNetworkModel.Active(
+ 1 /* networkId */,
+ true /* isValidated */,
+ 3 /* level */,
+ "test" /* ssid */,
+ WifiNetworkModel.HotspotDeviceType.NONE,
+ false /* isPasspointAccessPoint */,
+ false /* isOnlineSignUpforPasspointAccessPoint */,
+ null /* passpointProviderFriendlyName */
+ ));
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateInactive_wifiConnectedNotCasting_newPipeline() {
+ createAndStartTileNewImpl();
+
+ mWifiRepository.setWifiNetwork(
+ new WifiNetworkModel.Active(
+ 1 /* networkId */,
+ true /* isValidated */,
+ 3 /* level */,
+ "test" /* ssid */,
+ WifiNetworkModel.HotspotDeviceType.NONE,
+ false /* isPasspointAccessPoint */,
+ false /* isOnlineSignUpforPasspointAccessPoint */,
+ null /* passpointProviderFriendlyName */
+ ));
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ }
+
+ // -------------------------------------------------
+
+ // -------------------------------------------------
// All these tests for enabled/disabled hotspot have wifi not enabled
@Test
public void testStateUnavailable_hotspotDisabled() {
+ createAndStartTileOldImpl();
mHotspotCallback.onHotspotChanged(false, 0);
mTestableLooper.processAllMessages();
@@ -221,6 +282,7 @@
@Test
public void testStateUnavailable_hotspotEnabledNotConnected() {
+ createAndStartTileOldImpl();
mHotspotCallback.onHotspotChanged(true, 0);
mTestableLooper.processAllMessages();
@@ -229,6 +291,7 @@
@Test
public void testStateActive_hotspotEnabledAndConnectedAndCasting() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastController.CastDevice.STATE_CONNECTED;
List<CastDevice> devices = new ArrayList<>();
@@ -242,6 +305,7 @@
@Test
public void testStateInactive_hotspotEnabledAndConnectedAndNotCasting() {
+ createAndStartTileOldImpl();
mHotspotCallback.onHotspotChanged(true, 1);
mTestableLooper.processAllMessages();
assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
@@ -250,6 +314,7 @@
@Test
public void testHandleClick_castDevicePresent() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaRouter.RouteInfo.class);
@@ -267,6 +332,7 @@
@Test
public void testHandleClick_projectionOnly() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaProjectionInfo.class);
@@ -283,6 +349,7 @@
@Test
public void testUpdateState_projectionOnly() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaProjectionInfo.class);
@@ -298,6 +365,7 @@
@Test
public void testUpdateState_castingAndProjection() {
+ createAndStartTileOldImpl();
CastController.CastDevice casting = new CastController.CastDevice();
casting.state = CastDevice.STATE_CONNECTED;
casting.tag = mock(RouteInfo.class);
@@ -322,6 +390,7 @@
@Test
public void testUpdateState_connectedAndConnecting() {
+ createAndStartTileOldImpl();
CastController.CastDevice connecting = new CastController.CastDevice();
connecting.state = CastDevice.STATE_CONNECTING;
connecting.tag = mock(RouteInfo.class);
@@ -346,6 +415,7 @@
@Test
public void testExpandView_wifiNotConnected() {
+ createAndStartTileOldImpl();
mCastTile.refreshState();
mTestableLooper.processAllMessages();
@@ -354,6 +424,7 @@
@Test
public void testExpandView_wifiEnabledNotCasting() {
+ createAndStartTileOldImpl();
enableWifiAndProcessMessages();
assertTrue(mCastTile.getState().forceExpandIcon);
@@ -361,6 +432,7 @@
@Test
public void testExpandView_casting_projection() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastController.CastDevice.STATE_CONNECTED;
List<CastDevice> devices = new ArrayList<>();
@@ -374,6 +446,7 @@
@Test
public void testExpandView_connecting_projection() {
+ createAndStartTileOldImpl();
CastController.CastDevice connecting = new CastController.CastDevice();
connecting.state = CastDevice.STATE_CONNECTING;
connecting.name = "Test Casting Device";
@@ -389,6 +462,7 @@
@Test
public void testExpandView_casting_mediaRoute() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaRouter.RouteInfo.class);
@@ -403,6 +477,7 @@
@Test
public void testExpandView_connecting_mediaRoute() {
+ createAndStartTileOldImpl();
CastController.CastDevice connecting = new CastController.CastDevice();
connecting.state = CastDevice.STATE_CONNECTING;
connecting.tag = mock(RouteInfo.class);
@@ -416,4 +491,86 @@
assertTrue(mCastTile.getState().forceExpandIcon);
}
+
+ /**
+ * For simplicity, let this method still set the field even though that's kind of gross
+ */
+ private void createAndStartTileOldImpl() {
+ mFeatureFlags.set(SIGNAL_CALLBACK_DEPRECATION, false);
+ mCastTile = new CastTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguard,
+ mNetworkController,
+ mHotspotController,
+ mDialogLaunchAnimator,
+ mWifiInteractor,
+ mJavaAdapter,
+ mFeatureFlags
+ );
+ mCastTile.initialize();
+
+ // We are not setting the mocks to listening, so we trigger a first refresh state to
+ // set the initial state
+ mCastTile.refreshState();
+
+ mTestableLooper.processAllMessages();
+
+ mCastTile.handleSetListening(true);
+ ArgumentCaptor<SignalCallback> signalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(SignalCallback.class);
+ verify(mNetworkController).observe(any(LifecycleOwner.class),
+ signalCallbackArgumentCaptor.capture());
+ mSignalCallback = signalCallbackArgumentCaptor.getValue();
+
+ ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(HotspotController.Callback.class);
+ verify(mHotspotController).observe(any(LifecycleOwner.class),
+ hotspotCallbackArgumentCaptor.capture());
+ mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
+ }
+
+ private void createAndStartTileNewImpl() {
+ mFeatureFlags.set(SIGNAL_CALLBACK_DEPRECATION, true);
+ mCastTile = new CastTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguard,
+ mNetworkController,
+ mHotspotController,
+ mDialogLaunchAnimator,
+ mWifiInteractor,
+ mJavaAdapter,
+ mFeatureFlags
+ );
+ mCastTile.initialize();
+
+ // Since we do not capture the callbacks like in the old impl, set the state to RESUMED
+ // So that TileJavaAdapter is collecting on flows
+ mCastTile.setListening(new Object(), true);
+
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(HotspotController.Callback.class);
+ verify(mHotspotController).observe(any(LifecycleOwner.class),
+ hotspotCallbackArgumentCaptor.capture());
+ mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
new file mode 100644
index 0000000..b6cf459
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.os.Handler
+import android.service.quicksettings.Tile
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
+import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+class InternetTileNewImplTest : SysuiTestCase() {
+ lateinit var underTest: InternetTileNewImpl
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private var airplaneModeRepository = FakeAirplaneModeRepository()
+ private var connectivityRepository = FakeConnectivityRepository()
+ private var ethernetInteractor = EthernetInteractor(connectivityRepository)
+ private var mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private var wifiRepository = FakeWifiRepository()
+ private var wifiInteractor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
+ private lateinit var viewModel: InternetTileViewModel
+
+ private lateinit var looper: TestableLooper
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var eventLogger: QsEventLogger
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var sbStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var logger: QSLogger
+ @Mock private lateinit var dialogFactory: InternetDialogFactory
+ @Mock private lateinit var accessPointController: AccessPointController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ looper = TestableLooper.get(this)
+
+ // Allow the tile to load resources
+ whenever(host.context).thenReturn(context)
+ whenever(host.userContext).thenReturn(context)
+
+ viewModel =
+ InternetTileViewModel(
+ airplaneModeRepository,
+ connectivityRepository,
+ ethernetInteractor,
+ mobileIconsInteractor,
+ wifiInteractor,
+ context,
+ testScope.backgroundScope,
+ )
+
+ underTest =
+ InternetTileNewImpl(
+ host,
+ eventLogger,
+ looper.looper,
+ Handler(looper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ sbStateController,
+ activityStarter,
+ logger,
+ viewModel,
+ dialogFactory,
+ accessPointController
+ )
+
+ underTest.initialize()
+ underTest.setListening(Object(), true)
+
+ looper.processAllMessages()
+ }
+
+ @Test
+ fun noDefaultConnection_noNetworkAvailable() =
+ testScope.runTest {
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.wifiScanResults.value = listOf()
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.secondaryLabel.toString())
+ .isEqualTo(context.getString(R.string.quick_settings_networks_unavailable))
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun noDefaultConnection_networksAvailable() =
+ testScope.runTest {
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ )
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.secondaryLabel.toString())
+ .isEqualTo(context.getString(R.string.quick_settings_networks_available))
+ assertThat(underTest.state.state).isEqualTo(1)
+ }
+
+ @Test
+ fun airplaneMode_enabled_wifiDisabled() =
+ testScope.runTest {
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.setIsWifiEnabled(false)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ assertThat(underTest.state.secondaryLabel)
+ .isEqualTo(context.getString(R.string.status_bar_airplane))
+ }
+
+ @Test
+ fun airplaneMode_enabled_wifiEnabledButNotConnected() =
+ testScope.runTest {
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.setIsWifiEnabled(true)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ assertThat(underTest.state.secondaryLabel)
+ .isEqualTo(context.getString(R.string.status_bar_airplane))
+ }
+
+ @Test
+ fun airplaneMode_enabled_wifiEnabledAndConnected() =
+ testScope.runTest {
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.defaultConnections.value =
+ DefaultConnectionModel(
+ wifi = Wifi(true),
+ isValidated = true,
+ )
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setWifiNetwork(ACTIVE_WIFI)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
+ }
+
+ @Test
+ fun wifiConnected() =
+ testScope.runTest {
+ connectivityRepository.defaultConnections.value =
+ DefaultConnectionModel(
+ wifi = Wifi(true),
+ isValidated = true,
+ )
+
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setWifiNetwork(ACTIVE_WIFI)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
+ }
+
+ companion object {
+ const val WIFI_SSID = "test ssid"
+ val ACTIVE_WIFI =
+ WifiNetworkModel.Active(
+ networkId = 1,
+ isValidated = true,
+ level = 4,
+ ssid = WIFI_SSID,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 46cbfac..0ecbcde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -127,7 +127,7 @@
bouncerInteractor = bouncerInteractor,
)
- private val keyguardRepository = utils.keyguardRepository()
+ private val keyguardRepository = utils.keyguardRepository
private val keyguardInteractor =
utils.keyguardInteractor(
repository = keyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 713c602..8620f61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -147,4 +147,11 @@
underTest.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
+
+ @Test
+ fun userInput() =
+ testScope.runTest {
+ underTest.onUserInput()
+ assertThat(utils.powerRepository.userTouchRegistered).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 951cadd..d60d994 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -61,7 +61,7 @@
utils.authenticationInteractor(
repository = authenticationRepository,
)
- private val keyguardRepository = utils.keyguardRepository()
+ private val keyguardRepository = utils.keyguardRepository
private val keyguardInteractor =
utils.keyguardInteractor(
repository = keyguardRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 981e44b..209dcc1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -145,6 +145,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -322,6 +323,7 @@
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
@Mock private KeyguardRootView mKeyguardRootView;
+ @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@@ -650,6 +652,7 @@
mKeyuardLongPressViewModel,
mKeyguardInteractor,
mActivityStarter,
+ mSharedNotificationContainerInteractor,
mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor,
mKeyguardRootView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index a2c2912..e22e571 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -49,7 +49,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.shade.data.repository.ShadeRepository;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -147,6 +147,7 @@
protected FakeDisableFlagsRepository mDisableFlagsRepository =
new FakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
+ protected FakeShadeRepository mShadeRepository = new FakeShadeRepository();
protected SysuiStatusBarStateController mStatusBarStateController;
protected ShadeInteractor mShadeInteractor;
@@ -174,7 +175,8 @@
mKeyguardRepository,
new FakeUserSetupRepository(),
mDeviceProvisionedController,
- mUserInteractor
+ mUserInteractor,
+ mShadeRepository
);
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
@@ -249,7 +251,7 @@
mShadeLogger,
mDumpManager,
mock(KeyguardFaceAuthInteractor.class),
- mock(ShadeRepository.class),
+ mShadeRepository,
mShadeInteractor,
new JavaAdapter(mTestScope.getBackgroundScope()),
mCastController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index cdcd1a2..e6e7482 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
@@ -69,6 +70,7 @@
private val userRepository = FakeUserRepository()
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
+ private val shadeRepository = FakeShadeRepository()
@Mock private lateinit var manager: UserManager
@Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
@@ -143,6 +145,7 @@
userSetupRepository,
deviceProvisionedController,
userInteractor,
+ shadeRepository,
)
}
@@ -353,4 +356,29 @@
// THEN expand is enabled
assertThat(actual).isTrue()
}
+
+ @Test
+ fun fullShadeExpansionWhenShadeLocked() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ shadeRepository.setShadeExpansion(0.5f)
+
+ assertThat(actual).isEqualTo(1f)
+ }
+
+ @Test
+ fun fullShadeExpansionWhenStatusBarStateIsNotShadeLocked() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ shadeRepository.setShadeExpansion(0.5f)
+ assertThat(actual).isEqualTo(0.5f)
+
+ shadeRepository.setShadeExpansion(0.8f)
+ assertThat(actual).isEqualTo(0.8f)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index e26a8bd..fdaea22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -58,9 +58,8 @@
MockitoAnnotations.initMocks(this)
underTest = ShadeRepositoryImpl(shadeExpansionStateManager)
- `when`(shadeExpansionStateManager.addExpansionListener(any())).thenReturn(
- ShadeExpansionChangeEvent(0f, false, false, 0f)
- )
+ `when`(shadeExpansionStateManager.addExpansionListener(any()))
+ .thenReturn(ShadeExpansionChangeEvent(0f, false, false, 0f))
}
@Test
@@ -122,6 +121,21 @@
}
@Test
+ fun updateShadeExpansion() =
+ testScope.runTest {
+ assertThat(underTest.shadeExpansion.value).isEqualTo(0f)
+
+ underTest.setShadeExpansion(.5f)
+ assertThat(underTest.shadeExpansion.value).isEqualTo(.5f)
+
+ underTest.setShadeExpansion(.82f)
+ assertThat(underTest.shadeExpansion.value).isEqualTo(.82f)
+
+ underTest.setShadeExpansion(1f)
+ assertThat(underTest.shadeExpansion.value).isEqualTo(1f)
+ }
+
+ @Test
fun updateUdfpsTransitionToFullShadeProgress() =
testScope.runTest {
assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 4a2518a..51e72c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -60,8 +60,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -102,21 +102,24 @@
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
- private val shadeInteractor = ShadeInteractor(
- testScope.backgroundScope,
- disableFlagsRepository,
- keyguardRepository,
- userSetupRepository = FakeUserSetupRepository(),
- deviceProvisionedController = mock(),
- userInteractor = mock(),
- )
- private val powerInteractor = PowerInteractor(
- FakePowerRepository(),
- keyguardRepository,
- FalsingCollectorFake(),
- screenOffAnimationController = mock(),
- statusBarStateController = mock(),
- )
+ private val shadeInteractor =
+ ShadeInteractor(
+ testScope.backgroundScope,
+ disableFlagsRepository,
+ keyguardRepository,
+ userSetupRepository = FakeUserSetupRepository(),
+ deviceProvisionedController = mock(),
+ userInteractor = mock(),
+ repository = FakeShadeRepository(),
+ )
+ private val powerInteractor =
+ PowerInteractor(
+ FakePowerRepository(),
+ keyguardRepository,
+ FalsingCollectorFake(),
+ screenOffAnimationController = mock(),
+ statusBarStateController = mock(),
+ )
@JvmField @Rule val mockito = MockitoJUnit.rule()
private val configurationController = FakeConfigurationController()
@@ -127,14 +130,13 @@
disableFlagsRepository.disableFlags.value = DisableFlagsModel()
testScope.runCurrent()
- val helper = NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this))
+ val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
row = helper.createRow()
- context.getOrCreateTestableResources()
- .addOverride(R.bool.config_use_split_notification_shade, false)
- context.getOrCreateTestableResources()
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, false)
+ context
+ .getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
transitionController =
LockscreenShadeTransitionController(
@@ -154,15 +156,20 @@
splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
singleShadeOverScrollerFactory = { singleShadeOverScroller },
scrimTransitionController =
- LockscreenShadeScrimTransitionController(
- scrimController, context, configurationController, dumpManager),
+ LockscreenShadeScrimTransitionController(
+ scrimController,
+ context,
+ configurationController,
+ dumpManager
+ ),
keyguardTransitionControllerFactory = { notificationPanelController ->
LockscreenShadeKeyguardTransitionController(
mediaHierarchyManager,
notificationPanelController,
context,
configurationController,
- dumpManager)
+ dumpManager
+ )
},
qsTransitionControllerFactory = { qsTransitionController },
activityStarter = activityStarter,
@@ -180,8 +187,8 @@
whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(nsslController.isInLockedDownShade).thenReturn(false)
whenever(qS.isFullyCollapsed).thenReturn(true)
- whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
- true)
+ whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt()))
+ .thenReturn(true)
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
@@ -228,8 +235,10 @@
whenever(statusbarStateController.isDozing).thenReturn(false)
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- assertFalse("Waking to shade locked when not dozing",
- transitionController.isWakingToShadeLocked)
+ assertFalse(
+ "Waking to shade locked when not dozing",
+ transitionController.isWakingToShadeLocked
+ )
}
@Test
@@ -243,9 +252,10 @@
@Test
fun testDontGoWhenShadeDisabled() {
- disableFlagsRepository.disableFlags.value = DisableFlagsModel(
- disable2 = DISABLE2_NOTIFICATION_SHADE,
- )
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NOTIFICATION_SHADE,
+ )
testScope.runCurrent()
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
@@ -298,8 +308,8 @@
verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
- verify(transitionControllerCallback, never()).setTransitionToFullShadeAmount(anyFloat(),
- anyBoolean(), anyLong())
+ verify(transitionControllerCallback, never())
+ .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
verify(qsTransitionController, never()).dragDownAmount = anyFloat()
}
@@ -309,15 +319,16 @@
verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
- verify(transitionControllerCallback).setTransitionToFullShadeAmount(anyFloat(),
- anyBoolean(), anyLong())
+ verify(transitionControllerCallback)
+ .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
verify(qsTransitionController).dragDownAmount = 10f
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
@Test
fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() {
- context.getOrCreateTestableResources()
+ context
+ .getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
configurationController.notifyConfigurationChanged()
@@ -328,7 +339,8 @@
@Test
fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() {
- context.getOrCreateTestableResources()
+ context
+ .getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
configurationController.notifyConfigurationChanged()
@@ -346,13 +358,14 @@
@Test
fun setDragAmount_setsKeyguardAlphaBasedOnDistance() {
- val alphaDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
+ val alphaDistance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ )
transitionController.dragDownAmount = 10f
val expectedAlpha = 1 - 10f / alphaDistance
- verify(shadeViewController)
- .setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
+ verify(shadeViewController).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
}
@Test
@@ -378,8 +391,7 @@
transitionController.dragDownAmount = 10f
- verify(shadeViewController)
- .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
+ verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
}
@Test
@@ -392,10 +404,12 @@
val distance =
context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_keyguard_transition_distance)
+ R.dimen.lockscreen_shade_keyguard_transition_distance
+ )
val offset =
context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_keyguard_transition_vertical_offset)
+ R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
+ )
val expectedTranslation = 10f / distance * offset
verify(shadeViewController)
.setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
@@ -411,16 +425,19 @@
@Test
fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
val distance = 10
- context.orCreateTestableResources
- .addOverride(R.dimen.lockscreen_shade_scrim_transition_distance, distance)
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_scrim_transition_distance,
+ distance
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 5f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = eq(0.5f),
lockScreenNotificationsProgress = anyFloat()
- )
+ )
}
@Test
@@ -428,17 +445,22 @@
val distance = 100
val delay = 10
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
+ )
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 20f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(0.1f)
- )
+ )
}
@Test
@@ -446,17 +468,22 @@
val distance = 100
val delay = 50
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
+ )
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 20f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(0f)
- )
+ )
}
@Test
@@ -464,17 +491,22 @@
val distance = 100
val delay = 50
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
+ )
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 999999f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(1f)
- )
+ )
}
@Test
@@ -508,8 +540,10 @@
@Test
fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() {
- val alphaDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
+ val alphaDistance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ )
val dragDownAmount = 10f
enableSplitShade()
@@ -549,9 +583,6 @@
progress: Float,
lockScreenNotificationsProgress: Float
) {
- scrimController.setTransitionToFullShadeProgress(
- progress,
- lockScreenNotificationsProgress
- )
+ scrimController.setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index afd9954..55b52dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -21,122 +21,369 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
+ private val testScope = TestScope(StandardTestDispatcher())
+
+ private val disableFlagsRepository = FakeDisableFlagsRepository()
+ private val userSetupRepository = FakeUserSetupRepository()
+ private val shadeRepository = FakeShadeRepository()
+ private val keyguardRepository = FakeKeyguardRepository()
+
private lateinit var configurationRepository: FakeConfigurationRepository
private lateinit var sharedNotificationContainerInteractor:
SharedNotificationContainerInteractor
private lateinit var underTest: SharedNotificationContainerViewModel
+ private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var shadeInteractor: ShadeInteractor
+
+ @Mock private lateinit var notificationStackSizeCalculator: NotificationStackSizeCalculator
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var userInteractor: UserInteractor
+ @Mock
+ private lateinit var notificationStackScrollLayoutController:
+ NotificationStackScrollLayoutController
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(notificationStackScrollLayoutController.getView()).thenReturn(mock())
+ whenever(notificationStackScrollLayoutController.getShelfHeight()).thenReturn(0)
+
configurationRepository = FakeConfigurationRepository()
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ )
+ .also {
+ keyguardInteractor = it.keyguardInteractor
+ keyguardTransitionInteractor = it.keyguardTransitionInteractor
+ keyguardTransitionRepository = it.repository
+ }
+
+ shadeInteractor =
+ ShadeInteractor(
+ testScope.backgroundScope,
+ disableFlagsRepository,
+ keyguardRepository,
+ userSetupRepository,
+ deviceProvisionedController,
+ userInteractor,
+ shadeRepository,
+ )
+
sharedNotificationContainerInteractor =
SharedNotificationContainerInteractor(
configurationRepository,
mContext,
)
- underTest = SharedNotificationContainerViewModel(sharedNotificationContainerInteractor)
+ underTest =
+ SharedNotificationContainerViewModel(
+ sharedNotificationContainerInteractor,
+ keyguardInteractor,
+ keyguardTransitionInteractor,
+ notificationStackSizeCalculator,
+ notificationStackScrollLayoutController,
+ shadeInteractor
+ )
}
@Test
- fun validateMarginStartInSplitShade() = runTest {
- overrideResource(R.bool.config_use_split_notification_shade, true)
- overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
+ fun validateMarginStartInSplitShade() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginStart).isEqualTo(0)
- }
+ assertThat(dimens!!.marginStart).isEqualTo(0)
+ }
@Test
- fun validateMarginStart() = runTest {
- overrideResource(R.bool.config_use_split_notification_shade, false)
- overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
+ fun validateMarginStart() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginStart).isEqualTo(20)
- }
+ assertThat(dimens!!.marginStart).isEqualTo(20)
+ }
@Test
- fun validateMarginEnd() = runTest {
- overrideResource(R.dimen.notification_panel_margin_horizontal, 50)
+ fun validateMarginEnd() =
+ testScope.runTest {
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 50)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginEnd).isEqualTo(50)
- }
+ assertThat(dimens!!.marginEnd).isEqualTo(50)
+ }
@Test
- fun validateMarginBottom() = runTest {
- overrideResource(R.dimen.notification_panel_margin_bottom, 50)
+ fun validateMarginBottom() =
+ testScope.runTest {
+ overrideResource(R.dimen.notification_panel_margin_bottom, 50)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginBottom).isEqualTo(50)
- }
+ assertThat(dimens!!.marginBottom).isEqualTo(50)
+ }
@Test
- fun validateMarginTopWithLargeScreenHeader() = runTest {
- overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.large_screen_shade_header_height, 50)
- overrideResource(R.dimen.notification_panel_margin_top, 0)
+ fun validateMarginTopWithLargeScreenHeader() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_large_screen_shade_header, true)
+ overrideResource(R.dimen.large_screen_shade_header_height, 50)
+ overrideResource(R.dimen.notification_panel_margin_top, 0)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginTop).isEqualTo(50)
- }
+ assertThat(dimens!!.marginTop).isEqualTo(50)
+ }
@Test
- fun validateMarginTop() = runTest {
- overrideResource(R.bool.config_use_large_screen_shade_header, false)
- overrideResource(R.dimen.large_screen_shade_header_height, 50)
- overrideResource(R.dimen.notification_panel_margin_top, 0)
+ fun validateMarginTop() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_large_screen_shade_header, false)
+ overrideResource(R.dimen.large_screen_shade_header_height, 50)
+ overrideResource(R.dimen.notification_panel_margin_top, 0)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
+ assertThat(dimens!!.marginTop).isEqualTo(0)
+ }
- assertThat(lastDimens.marginTop).isEqualTo(0)
+ @Test
+ fun isOnLockscreen() =
+ testScope.runTest {
+ val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(to = KeyguardState.GONE, transitionState = TransitionState.FINISHED)
+ )
+ assertThat(isOnLockscreen).isFalse()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ assertThat(isOnLockscreen).isTrue()
+ }
+
+ @Test
+ fun isOnLockscreenWithoutShade() =
+ testScope.runTest {
+ val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
+
+ // First on AOD
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0f)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ // Now move to lockscreen
+ showLockscreen()
+
+ // While state is LOCKSCREEN, validate variations of both shade and qs expansion
+ shadeRepository.setShadeExpansion(0.1f)
+ shadeRepository.setQsExpansion(0f)
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ shadeRepository.setShadeExpansion(0.1f)
+ shadeRepository.setQsExpansion(0.1f)
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0.1f)
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0f)
+ assertThat(isOnLockscreenWithoutShade).isTrue()
+ }
+
+ @Test
+ fun positionOnLockscreenNotInSplitShade() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen
+ showLockscreen()
+
+ // When not in split shade
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ assertThat(position)
+ .isEqualTo(SharedNotificationContainerPosition(top = 1f, bottom = 2f))
+ }
+
+ @Test
+ fun positionOnLockscreenInSplitShade() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen
+ showLockscreen()
+
+ // When in split shade
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ runCurrent()
+
+ // Top should be overridden to 0f
+ assertThat(position)
+ .isEqualTo(SharedNotificationContainerPosition(top = 0f, bottom = 2f))
+ }
+
+ @Test
+ fun positionOnShade() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen with shade expanded
+ showLockscreenWithShadeExpanded()
+
+ // When not in split shade
+ sharedNotificationContainerInteractor.setTopPosition(10f)
+
+ assertThat(position)
+ .isEqualTo(SharedNotificationContainerPosition(top = 10f, bottom = 0f))
+ }
+
+ @Test
+ fun maxNotificationsOnLockscreen() =
+ testScope.runTest {
+ whenever(
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ )
+ .thenReturn(10)
+
+ val maxNotifications by collectLastValue(underTest.maxNotifications)
+
+ showLockscreen()
+
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ assertThat(maxNotifications).isEqualTo(10)
+ }
+
+ @Test
+ fun maxNotificationsOnShade() =
+ testScope.runTest {
+ whenever(
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ )
+ .thenReturn(10)
+ val maxNotifications by collectLastValue(underTest.maxNotifications)
+
+ // Show lockscreen with shade expanded
+ showLockscreenWithShadeExpanded()
+
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ // -1 means No Limit
+ assertThat(maxNotifications).isEqualTo(-1)
+ }
+
+ private suspend fun showLockscreen() {
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0f)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ }
+
+ private suspend fun showLockscreenWithShadeExpanded() {
+ shadeRepository.setShadeExpansion(1f)
+ shadeRepository.setQsExpansion(0f)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED
+ )
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
new file mode 100644
index 0000000..5784be3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.ethernet.domain
+
+import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class EthernetInteractorTest : SysuiTestCase() {
+ private val connectivityRepository = FakeConnectivityRepository()
+ private val underTest = EthernetInteractor(connectivityRepository)
+
+ private val testScope = TestScope()
+
+ @Test
+ fun icon_default_validated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = true)
+
+ val expected =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet_fully,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
+ )
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun icon_default_notValidated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = false)
+
+ val expected =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
+ )
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun icon_notDefault_validated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = false, validated = true)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun icon_notDefault_notValidated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = false, validated = false)
+
+ assertThat(latest).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index ff28753..812e91b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -64,6 +64,28 @@
_dataEnabled.value = enabled
}
+ /**
+ * Set [primaryLevel] and [cdmaLevel]. Convenient when you don't care about the connection type
+ */
+ fun setAllLevels(level: Int) {
+ cdmaLevel.value = level
+ primaryLevel.value = level
+ }
+
+ /**
+ * Set both [isRoaming] and [cdmaRoaming] properties, in the event that you don't care about the
+ * connection type
+ */
+ fun setAllRoaming(roaming: Boolean) {
+ isRoaming.value = roaming
+ cdmaRoaming.value = roaming
+ }
+
+ /** Set the correct [resolvedNetworkType] for the given group via its lookup key */
+ fun setNetworkTypeKey(key: String) {
+ resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(key)
+ }
+
companion object {
const val DEFAULT_NETWORK_NAME = "default name"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 9c0cb17..ede02d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -674,6 +674,7 @@
val realRepo =
MobileConnectionRepositoryImpl(
SUB_ID,
+ context,
subscriptionModel,
DEFAULT_NAME_MODEL,
SEP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index e50e5e3..3af960b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -16,18 +16,22 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.content.BroadcastReceiver
+import android.content.Context
import android.content.Intent
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
import android.telephony.NetworkRegistrationInfo
import android.telephony.ServiceState
import android.telephony.ServiceState.STATE_IN_SERVICE
import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.DataActivityListener
import android.telephony.TelephonyCallback.ServiceStateListener
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED
import android.telephony.TelephonyManager.DATA_ACTIVITY_DORMANT
import android.telephony.TelephonyManager.DATA_ACTIVITY_IN
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
@@ -75,6 +79,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -88,6 +93,7 @@
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -100,6 +106,7 @@
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var tableLogger: TableLogBuffer
+ @Mock private lateinit var context: Context
private val mobileMappings = FakeMobileMappingsProxy()
private val systemUiCarrierConfig =
@@ -129,6 +136,7 @@
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
+ context,
subscriptionModel,
DEFAULT_NAME_MODEL,
SEP,
@@ -706,7 +714,9 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
@@ -720,13 +730,16 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
+
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, wrongSubIntent)
+ captor.value!!.onReceive(context, wrongSubIntent)
// THEN the previous intent's name is still used
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
@@ -741,7 +754,10 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
+
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
val intentWithoutInfo =
@@ -750,7 +766,7 @@
showPlmn = false,
)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intentWithoutInfo)
+ captor.value!!.onReceive(context, intentWithoutInfo)
assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
@@ -893,7 +909,7 @@
plmn: String = PLMN,
): Intent =
Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
- putExtra(EXTRA_SUBSCRIPTION_ID, subId)
+ putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
putExtra(EXTRA_SHOW_SPN, showSpn)
putExtra(EXTRA_SPN, spn)
putExtra(EXTRA_SHOW_PLMN, showPlmn)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index ea60aa7..852ed20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -125,6 +125,7 @@
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
+ context,
subscriptionModel,
DEFAULT_NAME,
SEP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index fd05cc4..6f9764a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -175,6 +175,7 @@
connectionFactory =
MobileConnectionRepositoryImpl.Factory(
+ context,
fakeBroadcastDispatcher,
telephonyManager = telephonyManager,
bgDispatcher = dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index a3df785..de2b6a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -16,12 +16,11 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
-import android.telephony.CellSignalStrength
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -30,8 +29,6 @@
) : MobileIconInteractor {
override val alwaysShowDataRatIcon = MutableStateFlow(false)
- override val alwaysUseCdmaLevel = MutableStateFlow(false)
-
override val activity =
MutableStateFlow(
DataActivityModel(
@@ -55,14 +52,8 @@
override val carrierName = MutableStateFlow("demo mode")
- private val _isEmergencyOnly = MutableStateFlow(false)
- override val isEmergencyOnly = _isEmergencyOnly
-
override val isRoaming = MutableStateFlow(false)
- private val _isFailedConnection = MutableStateFlow(false)
- override val isDefaultConnectionFailed = _isFailedConnection
-
override val isDataConnected = MutableStateFlow(true)
override val isInService = MutableStateFlow(true)
@@ -70,40 +61,21 @@
private val _isDataEnabled = MutableStateFlow(true)
override val isDataEnabled = _isDataEnabled
- private val _isDefaultDataEnabled = MutableStateFlow(true)
- override val isDefaultDataEnabled = _isDefaultDataEnabled
-
- private val _level = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
- override val level = _level
-
- private val _numberOfLevels = MutableStateFlow(DEFAULT_NUM_LEVELS)
- override val numberOfLevels = _numberOfLevels
-
override val isForceHidden = MutableStateFlow(false)
override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
- fun setIsEmergencyOnly(emergency: Boolean) {
- _isEmergencyOnly.value = emergency
- }
+ override val signalLevelIcon: MutableStateFlow<SignalIconModel> =
+ MutableStateFlow(
+ SignalIconModel(
+ level = 0,
+ numberOfLevels = 4,
+ showExclamationMark = false,
+ carrierNetworkChange = false,
+ )
+ )
fun setIsDataEnabled(enabled: Boolean) {
_isDataEnabled.value = enabled
}
-
- fun setIsDefaultDataEnabled(disabled: Boolean) {
- _isDefaultDataEnabled.value = disabled
- }
-
- fun setIsFailedConnection(failed: Boolean) {
- _isFailedConnection.value = failed
- }
-
- fun setLevel(level: Int) {
- _level.value = level
- }
-
- fun setNumberOfLevels(num: Int) {
- _numberOfLevels.value = num
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 82b7ec4..75d1869 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -58,6 +58,8 @@
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
+ override val activeDataIconInteractor = MutableStateFlow(null)
+
override val alwaysShowDataRatIcon = MutableStateFlow(false)
override val alwaysUseCdmaLevel = MutableStateFlow(false)
@@ -78,7 +80,7 @@
override val isForceHidden = MutableStateFlow(false)
/** Always returns a new fake interactor */
- override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
+ override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
return FakeMobileIconInteractor(tableLogBuffer).also { interactorCache[subId] = it }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index e3c59ad..e2f9119 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -75,22 +76,12 @@
@Before
fun setUp() {
underTest = createInteractor()
+
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
+ connectionRepository.isInService.value = true
}
@Test
- fun gsm_level_default_unknown() =
- testScope.runTest {
- connectionRepository.isGsm.value = true
-
- var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
-
- assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
-
- job.cancel()
- }
-
- @Test
fun gsm_usesGsmLevel() =
testScope.runTest {
connectionRepository.isGsm.value = true
@@ -98,7 +89,7 @@
connectionRepository.cdmaLevel.value = CDMA_LEVEL
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(GSM_LEVEL)
@@ -114,7 +105,7 @@
mobileIconsInteractor.alwaysUseCdmaLevel.value = true
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(GSM_LEVEL)
@@ -127,7 +118,7 @@
connectionRepository.isGsm.value = false
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
job.cancel()
@@ -142,7 +133,7 @@
mobileIconsInteractor.alwaysUseCdmaLevel.value = true
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(CDMA_LEVEL)
@@ -158,7 +149,7 @@
mobileIconsInteractor.alwaysUseCdmaLevel.value = false
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(GSM_LEVEL)
@@ -169,7 +160,7 @@
fun numberOfLevels_comesFromRepo() =
testScope.runTest {
var latest: Int? = null
- val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.numberOfLevels }.launchIn(this)
connectionRepository.numberOfLevels.value = 5
assertThat(latest).isEqualTo(5)
@@ -295,50 +286,6 @@
}
@Test
- fun alwaysUseCdmaLevel_matchesParent() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
-
- mobileIconsInteractor.alwaysUseCdmaLevel.value = true
- assertThat(latest).isTrue()
-
- mobileIconsInteractor.alwaysUseCdmaLevel.value = false
- assertThat(latest).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun test_isDefaultDataEnabled_matchesParent() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultDataEnabled.onEach { latest = it }.launchIn(this)
-
- mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
- assertThat(latest).isTrue()
-
- mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false
- assertThat(latest).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun test_isDefaultConnectionFailed_matchedParent() =
- testScope.runTest {
- val job = underTest.isDefaultConnectionFailed.launchIn(this)
-
- mobileIconsInteractor.isDefaultConnectionFailed.value = false
- assertThat(underTest.isDefaultConnectionFailed.value).isFalse()
-
- mobileIconsInteractor.isDefaultConnectionFailed.value = true
- assertThat(underTest.isDefaultConnectionFailed.value).isTrue()
-
- job.cancel()
- }
-
- @Test
fun dataState_connected() =
testScope.runTest {
var latest: Boolean? = null
@@ -541,6 +488,142 @@
assertThat(latest).isFalse()
}
+ @Test
+ fun iconId_correctLevel_notCutout() =
+ testScope.runTest {
+ connectionRepository.isInService.value = true
+ connectionRepository.primaryLevel.value = 1
+ connectionRepository.setDataEnabled(false)
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest?.level).isEqualTo(1)
+ assertThat(latest?.showExclamationMark).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesLevelFromInteractor() =
+ testScope.runTest {
+ connectionRepository.isInService.value = true
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.primaryLevel.value = 3
+ assertThat(latest!!.level).isEqualTo(3)
+
+ connectionRepository.primaryLevel.value = 1
+ assertThat(latest!!.level).isEqualTo(1)
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesNumberOfLevelsFromInteractor() =
+ testScope.runTest {
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.numberOfLevels.value = 5
+ assertThat(latest!!.numberOfLevels).isEqualTo(5)
+
+ connectionRepository.numberOfLevels.value = 2
+ assertThat(latest!!.numberOfLevels).isEqualTo(2)
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_defaultDataDisabled_showExclamationTrue() =
+ testScope.runTest {
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest!!.showExclamationMark).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_defaultConnectionFailed_showExclamationTrue() =
+ testScope.runTest {
+ mobileIconsInteractor.isDefaultConnectionFailed.value = true
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest!!.showExclamationMark).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_enabledAndNotFailed_showExclamationFalse() =
+ testScope.runTest {
+ connectionRepository.isInService.value = true
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
+ mobileIconsInteractor.isDefaultConnectionFailed.value = false
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest!!.showExclamationMark).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesEmptyState_whenNotInService() =
+ testScope.runTest {
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.isInService.value = false
+
+ assertThat(latest?.level).isEqualTo(0)
+ assertThat(latest?.showExclamationMark).isTrue()
+
+ // Changing the level doesn't overwrite the disabled state
+ connectionRepository.primaryLevel.value = 2
+ assertThat(latest?.level).isEqualTo(0)
+ assertThat(latest?.showExclamationMark).isTrue()
+
+ // Once back in service, the regular icon appears
+ connectionRepository.isInService.value = true
+ assertThat(latest?.level).isEqualTo(2)
+ assertThat(latest?.showExclamationMark).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() =
+ testScope.runTest {
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.isInService.value = true
+ connectionRepository.carrierNetworkChangeActive.value = true
+ connectionRepository.primaryLevel.value = 1
+ connectionRepository.cdmaLevel.value = 1
+
+ assertThat(latest!!.level).isEqualTo(1)
+ assertThat(latest!!.carrierNetworkChange).isTrue()
+
+ // SignalIconModel respects the current level
+ connectionRepository.primaryLevel.value = 2
+
+ assertThat(latest!!.level).isEqualTo(2)
+ assertThat(latest!!.carrierNetworkChange).isTrue()
+
+ job.cancel()
+ }
+
private fun createInteractor(
overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 3e6f909..b4c7578 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -781,6 +781,16 @@
job.cancel()
}
+ @Test
+ fun iconInteractor_cachedPerSubId() =
+ testScope.runTest {
+ val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+ val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+
+ assertThat(interactor1).isNotNull()
+ assertThat(interactor1).isSameInstanceAs(interactor2)
+ }
+
/**
* Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
* flow.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
index 01c388a..90a8946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index e59d90f..1878329 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -17,18 +17,26 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,11 +58,18 @@
private lateinit var homeIcon: HomeMobileIconViewModel
private lateinit var qsIcon: QsMobileIconViewModel
private lateinit var keyguardIcon: KeyguardMobileIconViewModel
- private lateinit var interactor: FakeMobileIconInteractor
+ private lateinit var iconsInteractor: MobileIconsInteractor
+ private lateinit var interactor: MobileIconInteractor
+ private lateinit var connectionsRepository: FakeMobileConnectionsRepository
+ private lateinit var repository: FakeMobileConnectionRepository
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
+
+ private val connectivityRepository = FakeConnectivityRepository()
+
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var constants: ConnectivityConstants
@Mock private lateinit var tableLogBuffer: TableLogBuffer
+ @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -67,16 +82,51 @@
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
)
- interactor = FakeMobileIconInteractor(tableLogBuffer)
- interactor.apply {
- setLevel(1)
- setIsDefaultDataEnabled(true)
- setIsFailedConnection(false)
- setIsEmergencyOnly(false)
- setNumberOfLevels(4)
- networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
- isDataConnected.value = true
- }
+ connectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+ repository =
+ FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
+ isInService.value = true
+ cdmaLevel.value = 1
+ primaryLevel.value = 1
+ isEmergencyOnly.value = false
+ numberOfLevels.value = 4
+ resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
+ dataConnectionState.value = DataConnectionState.Connected
+ }
+
+ connectionsRepository.activeMobileDataRepository.value = repository
+
+ connectivityRepository.apply { setMobileConnected() }
+
+ iconsInteractor =
+ MobileIconsInteractorImpl(
+ connectionsRepository,
+ carrierConfigTracker,
+ tableLogBuffer,
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ )
+
+ interactor =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ iconsInteractor.activeDataConnectionHasDataEnabled,
+ iconsInteractor.alwaysShowDataRatIcon,
+ iconsInteractor.alwaysUseCdmaLevel,
+ iconsInteractor.isSingleCarrier,
+ iconsInteractor.mobileIsDefault,
+ iconsInteractor.defaultMobileIconMapping,
+ iconsInteractor.defaultMobileIconGroup,
+ iconsInteractor.isDefaultConnectionFailed,
+ iconsInteractor.isForceHidden,
+ repository,
+ context,
+ MobileIconCarrierIdOverridesFake()
+ )
+
commonImpl =
MobileIconViewModel(
SUB_1_ID,
@@ -109,7 +159,7 @@
assertThat(latestQs).isEqualTo(expected)
assertThat(latestKeyguard).isEqualTo(expected)
- interactor.setLevel(2)
+ repository.setAllLevels(2)
expected = defaultSignal(level = 2)
assertThat(latestHome).isEqualTo(expected)
@@ -123,5 +173,16 @@
companion object {
private const val SUB_1_ID = 1
+ private const val NUM_LEVELS = 4
+
+ /** Convenience constructor for these tests */
+ fun defaultSignal(level: Int = 1): SignalIconModel {
+ return SignalIconModel(
+ level,
+ NUM_LEVELS,
+ showExclamationMark = false,
+ carrierNetworkChange = false,
+ )
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 72feec7..796d5ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -19,20 +19,30 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.TelephonyIcons.G
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
import com.android.settingslib.mobile.TelephonyIcons.UNKNOWN
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,12 +61,18 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class MobileIconViewModelTest : SysuiTestCase() {
+ private var connectivityRepository = FakeConnectivityRepository()
+
private lateinit var underTest: MobileIconViewModel
- private lateinit var interactor: FakeMobileIconInteractor
+ private lateinit var interactor: MobileIconInteractorImpl
+ private lateinit var iconsInteractor: MobileIconsInteractorImpl
+ private lateinit var repository: FakeMobileConnectionRepository
+ private lateinit var connectionsRepository: FakeMobileConnectionsRepository
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
@Mock private lateinit var constants: ConnectivityConstants
@Mock private lateinit var tableLogBuffer: TableLogBuffer
+ @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -66,23 +82,53 @@
MockitoAnnotations.initMocks(this)
whenever(constants.hasDataCapabilities).thenReturn(true)
+ connectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+
+ repository =
+ FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
+ setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ isInService.value = true
+ dataConnectionState.value = DataConnectionState.Connected
+ dataEnabled.value = true
+ }
+ connectionsRepository.activeMobileDataRepository.value = repository
+ connectionsRepository.mobileIsDefault.value = true
+
airplaneModeRepository = FakeAirplaneModeRepository()
airplaneModeInteractor =
AirplaneModeInteractor(
airplaneModeRepository,
- FakeConnectivityRepository(),
+ connectivityRepository,
)
- interactor = FakeMobileIconInteractor(tableLogBuffer)
- interactor.apply {
- setLevel(1)
- setIsDefaultDataEnabled(true)
- setIsFailedConnection(false)
- setIsEmergencyOnly(false)
- setNumberOfLevels(4)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- isDataConnected.value = true
- }
+ iconsInteractor =
+ MobileIconsInteractorImpl(
+ connectionsRepository,
+ carrierConfigTracker,
+ tableLogBuffer,
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ )
+
+ interactor =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ iconsInteractor.activeDataConnectionHasDataEnabled,
+ iconsInteractor.alwaysShowDataRatIcon,
+ iconsInteractor.alwaysUseCdmaLevel,
+ iconsInteractor.isSingleCarrier,
+ iconsInteractor.mobileIsDefault,
+ iconsInteractor.defaultMobileIconMapping,
+ iconsInteractor.defaultMobileIconGroup,
+ iconsInteractor.isDefaultConnectionFailed,
+ iconsInteractor.isForceHidden,
+ repository,
+ context,
+ MobileIconCarrierIdOverridesFake()
+ )
createAndSetViewModel()
}
@@ -108,7 +154,6 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(false)
- interactor.isForceHidden.value = false
assertThat(latest).isTrue()
@@ -122,8 +167,8 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(true)
- interactor.isAllowedDuringAirplaneMode.value = false
- interactor.isForceHidden.value = false
+ repository.isAllowedDuringAirplaneMode.value = false
+ connectivityRepository.setForceHiddenIcons(setOf())
assertThat(latest).isFalse()
@@ -138,8 +183,8 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(true)
- interactor.isAllowedDuringAirplaneMode.value = true
- interactor.isForceHidden.value = false
+ repository.isAllowedDuringAirplaneMode.value = true
+ connectivityRepository.setForceHiddenIcons(setOf())
assertThat(latest).isTrue()
@@ -153,7 +198,7 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(false)
- interactor.isForceHidden.value = true
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isFalse()
@@ -167,156 +212,29 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(false)
- interactor.isForceHidden.value = false
+ connectivityRepository.setForceHiddenIcons(setOf())
assertThat(latest).isTrue()
airplaneModeRepository.setIsAirplaneMode(true)
assertThat(latest).isFalse()
- interactor.isAllowedDuringAirplaneMode.value = true
+ repository.isAllowedDuringAirplaneMode.value = true
assertThat(latest).isTrue()
- interactor.isForceHidden.value = true
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isFalse()
job.cancel()
}
@Test
- fun iconId_correctLevel_notCutout() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
- val expected = defaultSignal()
-
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
-
- @Test
- fun icon_usesLevelFromInteractor() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.level.value = 3
- assertThat(latest!!.level).isEqualTo(3)
-
- interactor.level.value = 1
- assertThat(latest!!.level).isEqualTo(1)
-
- job.cancel()
- }
-
- @Test
- fun icon_usesNumberOfLevelsFromInteractor() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.numberOfLevels.value = 5
- assertThat(latest!!.numberOfLevels).isEqualTo(5)
-
- interactor.numberOfLevels.value = 2
- assertThat(latest!!.numberOfLevels).isEqualTo(2)
-
- job.cancel()
- }
-
- @Test
- fun icon_defaultDataDisabled_showExclamationTrue() =
- testScope.runTest {
- interactor.setIsDefaultDataEnabled(false)
-
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest!!.showExclamationMark).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun icon_defaultConnectionFailed_showExclamationTrue() =
- testScope.runTest {
- interactor.isDefaultConnectionFailed.value = true
-
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest!!.showExclamationMark).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun icon_enabledAndNotFailed_showExclamationFalse() =
- testScope.runTest {
- interactor.setIsDefaultDataEnabled(true)
- interactor.isDefaultConnectionFailed.value = false
-
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest!!.showExclamationMark).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun icon_usesEmptyState_whenNotInService() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.isInService.value = false
-
- var expected = emptySignal()
-
- assertThat(latest).isEqualTo(expected)
-
- // Changing the level doesn't overwrite the disabled state
- interactor.level.value = 2
- assertThat(latest).isEqualTo(expected)
-
- // Once back in service, the regular icon appears
- interactor.isInService.value = true
- expected = defaultSignal(level = 2)
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
-
- @Test
- fun icon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.carrierNetworkChangeActive.value = true
- interactor.level.value = 1
-
- assertThat(latest!!.level).isEqualTo(1)
- assertThat(latest!!.carrierNetworkChange).isTrue()
-
- // SignalIconModel respects the current level
- interactor.level.value = 2
-
- assertThat(latest!!.level).isEqualTo(2)
- assertThat(latest!!.carrierNetworkChange).isTrue()
-
- job.cancel()
- }
-
- @Test
fun contentDescription_notInService_usesNoPhone() =
testScope.runTest {
var latest: ContentDescription? = null
val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
- interactor.isInService.value = false
+ repository.isInService.value = false
assertThat((latest as ContentDescription.Resource).res)
.isEqualTo(PHONE_SIGNAL_STRENGTH_NONE)
@@ -330,13 +248,11 @@
var latest: ContentDescription? = null
val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
- interactor.isInService.value = true
-
- interactor.level.value = 2
+ repository.setAllLevels(2)
assertThat((latest as ContentDescription.Resource).res)
.isEqualTo(PHONE_SIGNAL_STRENGTH[2])
- interactor.level.value = 0
+ repository.setAllLevels(0)
assertThat((latest as ContentDescription.Resource).res)
.isEqualTo(PHONE_SIGNAL_STRENGTH[0])
@@ -351,7 +267,8 @@
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
+ connectionsRepository.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -364,9 +281,9 @@
@Test
fun networkType_null_whenDisabled() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(false)
- interactor.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.setDataEnabled(false)
+ connectionsRepository.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -378,9 +295,9 @@
@Test
fun networkType_null_whenCarrierNetworkChangeActive() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.carrierNetworkChangeActive.value = true
- interactor.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.carrierNetworkChangeActive.value = true
+ connectionsRepository.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -397,10 +314,10 @@
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(true)
- interactor.isDataConnected.value = true
- interactor.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.setDataEnabled(true)
+ repository.dataConnectionState.value = DataConnectionState.Connected
+ connectionsRepository.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -418,15 +335,13 @@
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
assertThat(latest).isEqualTo(initial)
- interactor.isDataConnected.value = false
- yield()
+ repository.dataConnectionState.value = DataConnectionState.Disconnected
assertThat(latest).isNull()
@@ -441,15 +356,13 @@
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(true)
+ repository.dataEnabled.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(expected)
- interactor.setIsDataEnabled(false)
- yield()
+ repository.dataEnabled.value = false
assertThat(latest).isNull()
@@ -459,9 +372,10 @@
@Test
fun networkType_alwaysShow_shownEvenWhenDisabled() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(false)
- interactor.alwaysShowDataRatIcon.value = true
+ repository.dataEnabled.value = false
+
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -479,9 +393,11 @@
@Test
fun networkType_alwaysShow_shownEvenWhenDisconnected() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.isDataConnected.value = false
- interactor.alwaysShowDataRatIcon.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.dataConnectionState.value = DataConnectionState.Disconnected
+
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -499,9 +415,10 @@
@Test
fun networkType_alwaysShow_shownEvenWhenFailedConnection() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsFailedConnection(true)
- interactor.alwaysShowDataRatIcon.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ connectionsRepository.mobileIsDefault.value = true
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -517,16 +434,24 @@
}
@Test
- fun networkType_alwaysShow_notShownWhenInvalidDataTypeIcon() =
+ fun networkType_alwaysShow_usesDefaultIconWhenInvalid() =
testScope.runTest {
- // The UNKNOWN icon group doesn't have a valid data type icon ID
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(UNKNOWN)
- interactor.alwaysShowDataRatIcon.value = true
+ // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the
+ // old pipeline was to use the default icon group if the map doesn't exist
+ repository.setNetworkTypeKey(UNKNOWN.name)
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
- assertThat(latest).isNull()
+ val expected =
+ Icon.Resource(
+ connectionsRepository.defaultMobileIconGroup.value.dataType,
+ ContentDescription.Resource(G.dataContentDescription)
+ )
+
+ assertThat(latest).isEqualTo(expected)
job.cancel()
}
@@ -534,9 +459,10 @@
@Test
fun networkType_alwaysShow_shownWhenNotDefault() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.mobileIsDefault.value = false
- interactor.alwaysShowDataRatIcon.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ connectionsRepository.mobileIsDefault.value = false
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -554,9 +480,9 @@
@Test
fun networkType_notShownWhenNotDefault() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.isDataConnected.value = true
- interactor.mobileIsDefault.value = false
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.dataConnectionState.value = DataConnectionState.Connected
+ connectionsRepository.mobileIsDefault.value = false
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -569,13 +495,14 @@
@Test
fun roaming() =
testScope.runTest {
- interactor.isRoaming.value = true
+ repository.setAllRoaming(true)
+
var latest: Boolean? = null
val job = underTest.roaming.onEach { latest = it }.launchIn(this)
assertThat(latest).isTrue()
- interactor.isRoaming.value = false
+ repository.setAllRoaming(false)
assertThat(latest).isFalse()
@@ -599,7 +526,7 @@
val containerJob =
underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this)
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = true,
hasActivityOut = true,
@@ -631,7 +558,7 @@
val containerJob =
underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = true,
hasActivityOut = false,
@@ -643,7 +570,7 @@
assertThat(outVisible).isFalse()
assertThat(containerVisible).isTrue()
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = true,
@@ -653,7 +580,7 @@
assertThat(outVisible).isTrue()
assertThat(containerVisible).isTrue()
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 065dfba..e42515e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -154,33 +154,6 @@
}
@Test
- fun caching_mobileIconInteractorIsReusedForSameSubId() =
- testScope.runTest {
- val interactor1 = underTest.mobileIconInteractorForSub(1)
- val interactor2 = underTest.mobileIconInteractorForSub(1)
-
- assertThat(interactor1).isSameInstanceAs(interactor2)
- }
-
- @Test
- fun caching_invalidInteractorssAreRemovedFromCacheWhenSubDisappears() =
- testScope.runTest {
- // Retrieve interactors to trigger caching
- val interactor1 = underTest.mobileIconInteractorForSub(1)
- val interactor2 = underTest.mobileIconInteractorForSub(2)
-
- // Both impls are cached
- assertThat(underTest.mobileIconInteractorSubIdCache)
- .containsExactly(1, interactor1, 2, interactor2)
-
- // SUB_1 is removed from the list...
- interactor.filteredSubscriptions.value = listOf(SUB_2)
-
- // ... and dropped from the cache
- assertThat(underTest.mobileIconInteractorSubIdCache).containsExactly(2, interactor2)
- }
-
- @Test
fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
testScope.runTest {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index 8f28cc0..e44ff8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -27,7 +27,7 @@
MutableStateFlow(emptySet())
override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = _forceHiddenIcons
- override val defaultConnections: StateFlow<DefaultConnectionModel> =
+ override val defaultConnections: MutableStateFlow<DefaultConnectionModel> =
MutableStateFlow(DefaultConnectionModel())
override val vcnSubId: MutableStateFlow<Int?> = MutableStateFlow(null)
@@ -35,4 +35,43 @@
fun setForceHiddenIcons(hiddenIcons: Set<ConnectivitySlot>) {
_forceHiddenIcons.value = hiddenIcons
}
+
+ /**
+ * Convenience for setting mobile data connected, disconnected, or validated. Defaults to
+ * setting mobile connected && validated, since the default state is disconnected && not
+ * validated
+ */
+ fun setMobileConnected(
+ default: Boolean = true,
+ validated: Boolean = true,
+ ) {
+ defaultConnections.value =
+ DefaultConnectionModel(
+ mobile = DefaultConnectionModel.Mobile(default),
+ isValidated = validated,
+ )
+ }
+
+ /** Similar convenience method for ethernet */
+ fun setEthernetConnected(
+ default: Boolean = true,
+ validated: Boolean = true,
+ ) {
+ defaultConnections.value =
+ DefaultConnectionModel(
+ ethernet = DefaultConnectionModel.Ethernet(default),
+ isValidated = validated,
+ )
+ }
+
+ fun setWifiConnected(
+ default: Boolean = true,
+ validated: Boolean = true,
+ ) {
+ defaultConnections.value =
+ DefaultConnectionModel(
+ wifi = DefaultConnectionModel.Wifi(default),
+ isValidated = validated,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
new file mode 100644
index 0000000..8150a31
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.model.SignalIcon
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel.Companion.NOT_CONNECTED_NETWORKS_UNAVAILABLE
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class InternetTileViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: InternetTileViewModel
+ private lateinit var mobileIconsInteractor: MobileIconsInteractor
+
+ private val airplaneModeRepository = FakeAirplaneModeRepository()
+ private val connectivityRepository = FakeConnectivityRepository()
+ private val ethernetInteractor = EthernetInteractor(connectivityRepository)
+ private val wifiRepository = FakeWifiRepository()
+ private val userSetupRepo = FakeUserSetupRepository()
+ private val testScope = TestScope()
+ private val wifiInteractor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
+
+ private val tableLogBuffer: TableLogBuffer = mock()
+ private val carrierConfigTracker: CarrierConfigTracker = mock()
+
+ private val mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+ private val mobileConnectionRepository =
+ FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer)
+
+ @Before
+ fun setUp() {
+ mobileConnectionRepository.apply {
+ setNetworkTypeKey(mobileConnectionsRepository.GSM_KEY)
+ isInService.value = true
+ dataConnectionState.value = DataConnectionState.Connected
+ dataEnabled.value = true
+ }
+
+ mobileConnectionsRepository.apply {
+ activeMobileDataRepository.value = mobileConnectionRepository
+ activeMobileDataSubscriptionId.value = SUB_1_ID
+ setMobileConnectionRepositoryMap(mapOf(SUB_1_ID to mobileConnectionRepository))
+ }
+
+ mobileIconsInteractor =
+ MobileIconsInteractorImpl(
+ mobileConnectionsRepository,
+ carrierConfigTracker,
+ tableLogBuffer,
+ connectivityRepository,
+ userSetupRepo,
+ testScope.backgroundScope,
+ context,
+ )
+
+ underTest =
+ InternetTileViewModel(
+ airplaneModeRepository,
+ connectivityRepository,
+ ethernetInteractor,
+ mobileIconsInteractor,
+ wifiInteractor,
+ context,
+ testScope.backgroundScope,
+ )
+ }
+
+ @Test
+ fun noDefault_noNetworksAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+
+ assertThat(latest?.secondaryLabel)
+ .isEqualTo(Text.Resource(R.string.quick_settings_networks_unavailable))
+ assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_unavailable)
+ }
+
+ @Test
+ fun noDefault_networksAvailable() =
+ testScope.runTest {
+ // TODO: support [WifiInteractor.areNetworksAvailable]
+ }
+
+ @Test
+ fun wifiDefaultAndActive() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel =
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 4,
+ ssid = "test ssid",
+ )
+
+ connectivityRepository.setWifiConnected()
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+
+ // Type is [Visible] since that is the only model that stores a resId
+ val expectedIcon: WifiIcon.Visible =
+ WifiIcon.fromModel(networkModel, context) as WifiIcon.Visible
+
+ assertThat(latest?.secondaryTitle).isEqualTo("test ssid")
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.icon).isEqualTo(ResourceIcon.get(expectedIcon.icon.res))
+ assertThat(latest?.iconId).isNull()
+ }
+
+ @Test
+ fun wifiDefaultAndNotActive_noNetworksAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel = WifiNetworkModel.Inactive
+
+ connectivityRepository.setWifiConnected(validated = false)
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+ wifiRepository.wifiScanResults.value = emptyList()
+
+ assertThat(latest).isEqualTo(NOT_CONNECTED_NETWORKS_UNAVAILABLE)
+ }
+
+ @Test
+ fun wifiDefaultAndNotActive_networksAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel = WifiNetworkModel.Inactive
+
+ connectivityRepository.setWifiConnected(validated = false)
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+ wifiRepository.wifiScanResults.value = listOf(WifiScanEntry("test 1"))
+
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.secondaryTitle)
+ .isEqualTo(context.getString(R.string.quick_settings_networks_available))
+ assertThat(latest?.icon).isNull()
+ assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_available)
+ }
+
+ @Test
+ fun mobileDefault_usesNetworkNameAndIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ connectivityRepository.setMobileConnected()
+ mobileConnectionsRepository.mobileIsDefault.value = true
+ mobileConnectionRepository.apply {
+ setAllLevels(3)
+ setAllRoaming(false)
+ networkName.value = NetworkNameModel.Default("test network")
+ }
+
+ assertThat(latest?.secondaryTitle).contains("test network")
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.icon).isInstanceOf(SignalIcon::class.java)
+ assertThat(latest?.iconId).isNull()
+ }
+
+ @Test
+ fun ethernetDefault_validated_matchesInteractor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+ val ethernetIcon by collectLastValue(ethernetInteractor.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = true)
+
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.secondaryTitle)
+ .isEqualTo(ethernetIcon!!.contentDescription.toString())
+ assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully)
+ assertThat(latest?.icon).isNull()
+ }
+
+ @Test
+ fun ethernetDefault_notValidated_matchesInteractor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+ val ethernetIcon by collectLastValue(ethernetInteractor.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = false)
+
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.secondaryTitle)
+ .isEqualTo(ethernetIcon!!.contentDescription.toString())
+ assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet)
+ assertThat(latest?.icon).isNull()
+ }
+
+ companion object {
+ const val SUB_1_ID = 1
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 4f7bb72..106b548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryHelper.ACTIVITY_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -39,6 +40,9 @@
private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT)
override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity
+ override val wifiScanResults: MutableStateFlow<List<WifiScanEntry>> =
+ MutableStateFlow(emptyList())
+
fun setIsWifiEnabled(enabled: Boolean) {
_isWifiEnabled.value = enabled
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index bea1154..c2e75aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -27,6 +27,7 @@
import android.net.TransportInfo
import android.net.VpnTransportInfo
import android.net.vcn.VcnTransportInfo
+import android.net.wifi.ScanResult
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.TrafficStateCallback
@@ -45,6 +46,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -1205,6 +1207,58 @@
.isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
}
+ @Test
+ fun wifiScanResults_containsSsidList() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ val scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ val expected =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ WifiScanEntry(ssid = "ssid 4"),
+ WifiScanEntry(ssid = "ssid 5"),
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun wifiScanResults_updates() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ var scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ // New scan representing no results
+ scanResults = emptyList()
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ assertThat(latest).isEmpty()
+ }
+
private fun createRepo(): WifiRepositoryImpl {
return WifiRepositoryImpl(
fakeBroadcastDispatcher,
@@ -1240,6 +1294,13 @@
return callbackCaptor.value!!
}
+ private fun getScanResultsCallback(): WifiManager.ScanResultsCallback {
+ testScope.runCurrent()
+ val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>()
+ verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture())
+ return callbackCaptor.value!!
+ }
+
private fun createWifiNetworkCapabilities(
transportInfo: TransportInfo,
isValidated: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
index 662e36a..afab623 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
+import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.UNKNOWN_SSID
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
@@ -32,6 +33,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -77,6 +79,7 @@
featureFlags,
testScope.backgroundScope,
executor,
+ dispatcher,
wifiPickerTrackerFactory,
wifiManager,
logger,
@@ -1137,6 +1140,58 @@
.isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
}
+ @Test
+ fun wifiScanResults_containsSsidList() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ val scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ val expected =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ WifiScanEntry(ssid = "ssid 4"),
+ WifiScanEntry(ssid = "ssid 5"),
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun wifiScanResults_updates() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ var scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ // New scan representing no results
+ scanResults = listOf()
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ assertThat(latest).isEmpty()
+ }
+
private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback {
testScope.runCurrent()
return callbackCaptor.value
@@ -1156,6 +1211,13 @@
}
}
+ private fun getScanResultsCallback(): WifiManager.ScanResultsCallback {
+ testScope.runCurrent()
+ val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>()
+ verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture())
+ return callbackCaptor.value!!
+ }
+
private companion object {
const val TITLE = "AB"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 6fe88c1..1db8065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -21,11 +21,13 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -56,7 +58,8 @@
fun setUp() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
- underTest = WifiInteractorImpl(connectivityRepository, wifiRepository)
+ underTest =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
}
@Test
@@ -300,4 +303,76 @@
job.cancel()
}
+
+ @Test
+ fun areNetworksAvailable_noneActive_noResults() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value = emptyList()
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun areNetworksAvailable_noneActive_nonEmptyResults() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ )
+
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun areNetworksAvailable_activeNetwork_resultsIncludeOtherNetworks() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ )
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ ssid = "ssid 2",
+ networkId = 1,
+ level = 2,
+ )
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun areNetworksAvailable_activeNetwork_onlyResultIsTheActiveNetwork() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 2"),
+ )
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ ssid = "ssid 2",
+ networkId = 1,
+ level = 2,
+ )
+ )
+
+ assertThat(latest).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 3f49935..a0d4d13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -82,8 +82,8 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(Dispatchers.Unconfined)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository, scope)
airplaneModeViewModel =
AirplaneModeViewModelImpl(
AirplaneModeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index e6724d8..1d1b84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -40,7 +40,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -83,8 +83,8 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository, scope)
airplaneModeViewModel =
AirplaneModeViewModelImpl(
AirplaneModeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index bdeba2a..5aacc66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -74,7 +74,8 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
+ interactor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
airplaneModeViewModel =
AirplaneModeViewModelImpl(
AirplaneModeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c12df98..5256245 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -57,6 +57,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -71,6 +72,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -129,6 +131,9 @@
private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
+ @Rule
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -242,7 +247,6 @@
| AccessibilityManager.FLAG_CONTENT_TEXT);
}
-
@Test
public void testComputeTimeout_withHovering() {
Mockito.reset(mAccessibilityMgr);
@@ -669,11 +673,12 @@
@After
public void teardown() {
- cleanUp(mDialog);
setOrientation(mOriginalOrientation);
+ mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration);
mTestableLooper.processAllMessages();
reset(mPostureController);
+ cleanUp(mDialog);
}
private void cleanUp(VolumeDialogImpl dialog) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index b2a1668..72cdbbc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -16,22 +16,21 @@
package com.android.systemui.common.ui.data.repository
-import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.receiveAsFlow
class FakeConfigurationRepository : ConfigurationRepository {
- private val onAnyConfigurationChangeChannel = Channel<Unit>()
- override val onAnyConfigurationChange: Flow<Unit> =
- onAnyConfigurationChangeChannel.receiveAsFlow()
+ private val _onAnyConfigurationChange = MutableSharedFlow<Unit>()
+ override val onAnyConfigurationChange: Flow<Unit> = _onAnyConfigurationChange.asSharedFlow()
private val _scaleForResolution = MutableStateFlow(1f)
override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow()
suspend fun onAnyConfigurationChange() {
- onAnyConfigurationChangeChannel.send(Unit)
+ _onAnyConfigurationChange.tryEmit(Unit)
}
fun setScaleForResolution(scale: Float) {
@@ -41,4 +40,8 @@
override fun getResolutionScale(): Float {
return _scaleForResolution.value
}
+
+ override fun getDimensionPixelSize(id: Int): Int {
+ throw IllegalStateException("Don't use for tests")
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index f13c98d..8c1ef1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -23,6 +23,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.shade.data.repository.FakeShadeRepository
/**
* Simply put, I got tired of adding a constructor argument and then having to tweak dozens of
@@ -38,6 +39,7 @@
commandQueue: FakeCommandQueue = FakeCommandQueue(),
bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
+ shadeRepository: FakeShadeRepository = FakeShadeRepository(),
): WithDependencies {
return WithDependencies(
repository = repository,
@@ -45,12 +47,14 @@
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
+ shadeRepository = shadeRepository,
KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
+ shadeRepository = shadeRepository,
)
)
}
@@ -66,6 +70,7 @@
val featureFlags: FakeFeatureFlags,
val bouncerRepository: FakeKeyguardBouncerRepository,
val configurationRepository: FakeConfigurationRepository,
+ val shadeRepository: FakeShadeRepository,
val keyguardInteractor: KeyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
index 23faaf3..05c63b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.util.mockito.mock
import dagger.Lazy
import kotlinx.coroutines.CoroutineScope
@@ -31,7 +30,7 @@
@JvmStatic
fun create(
scope: CoroutineScope,
- repository: KeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
+ repository: FakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
keyguardInteractor: KeyguardInteractor =
KeyguardInteractorFactory.create().keyguardInteractor,
fromLockscreenTransitionInteractor: Lazy<FromLockscreenTransitionInteractor> = Lazy {
@@ -58,7 +57,7 @@
}
data class WithDependencies(
- val repository: KeyguardTransitionRepository,
+ val repository: FakeKeyguardTransitionRepository,
val keyguardInteractor: KeyguardInteractor,
val fromLockscreenTransitionInteractor: Lazy<FromLockscreenTransitionInteractor>,
val fromPrimaryBouncerTransitionInteractor: Lazy<FromPrimaryBouncerTransitionInteractor>,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
index 3334f3e..b92d946 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
@@ -32,6 +32,8 @@
var lastWakeWhy: String? = null
var lastWakeReason: Int? = null
+ var userTouchRegistered = false
+
fun setInteractive(value: Boolean) {
_isInteractive.value = value
}
@@ -40,4 +42,8 @@
lastWakeWhy = why
lastWakeReason = wakeReason
}
+
+ override fun userTouch() {
+ userTouchRegistered = true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index f0e1111..4f33a97 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -37,10 +37,12 @@
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.mockito.mock
@@ -92,6 +94,8 @@
}
}
+ val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
+
private val context = test.context
fun fakeSceneContainerRepository(
@@ -125,6 +129,7 @@
return SceneInteractor(
applicationScope = applicationScope(),
repository = repository,
+ powerRepository = powerRepository,
logger = mock(),
)
}
@@ -148,17 +153,14 @@
)
}
- fun keyguardRepository(): FakeKeyguardRepository {
- return keyguardRepository
- }
-
fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor {
return KeyguardInteractor(
repository = repository,
commandQueue = FakeCommandQueue(),
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
- configurationRepository = FakeConfigurationRepository()
+ configurationRepository = FakeConfigurationRepository(),
+ shadeRepository = FakeShadeRepository(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 492e542..ccddca2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -33,6 +33,9 @@
private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
+ private val _shadeExpansion = MutableStateFlow(0f)
+ override val shadeExpansion = _shadeExpansion
+
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
}
@@ -44,4 +47,8 @@
override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
_udfpsTransitionToFullShadeProgress.value = progress
}
+
+ override fun setShadeExpansion(expansion: Float) {
+ _shadeExpansion.value = expansion
+ }
}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 3fd6fe8..9bab261 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -78,11 +78,14 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import sun.misc.Unsafe;
+
/**
* <p>PinnerService pins important files for key processes in memory.</p>
* <p>Files to pin are specified in the config_defaultPinnerServiceFiles
@@ -150,6 +153,11 @@
@GuardedBy("this")
private ArraySet<Integer> mPinKeys;
+ private static final long MAX_ANON_SIZE = 2L * (1L << 30); // 2GB
+ private long mPinAnonSize;
+ private long mPinAnonAddress;
+ private long mCurrentlyPinnedAnonSize;
+
// Resource-configured pinner flags;
private final boolean mConfiguredToPinCamera;
private final boolean mConfiguredToPinHome;
@@ -550,6 +558,11 @@
pinKeys.add(KEY_ASSISTANT);
}
+ mPinAnonSize = DeviceConfig.getLong(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ "pin_anon_size",
+ SystemProperties.getLong("pinner.pin_anon_size", 0));
+ mPinAnonSize = Math.max(0, Math.min(mPinAnonSize, MAX_ANON_SIZE));
+
return pinKeys;
}
@@ -589,6 +602,7 @@
int key = currentPinKeys.valueAt(i);
pinApp(key, userHandle, true /* force */);
}
+ pinAnonRegion();
}
/**
@@ -673,6 +687,64 @@
}
/**
+ * Pin an empty anonymous region. This should only be used for ablation experiments.
+ */
+ private void pinAnonRegion() {
+ if (mPinAnonSize == 0) {
+ return;
+ }
+ long alignedPinSize = mPinAnonSize;
+ if (alignedPinSize % PAGE_SIZE != 0) {
+ alignedPinSize -= alignedPinSize % PAGE_SIZE;
+ Slog.e(TAG, "pinAnonRegion: aligning size to " + alignedPinSize);
+ }
+ if (mPinAnonAddress != 0
+ && mCurrentlyPinnedAnonSize != alignedPinSize) {
+ unpinAnonRegion();
+ }
+ long address = 0;
+ try {
+ address = Os.mmap(0, alignedPinSize,
+ OsConstants.PROT_READ | OsConstants.PROT_WRITE,
+ OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS,
+ new FileDescriptor(), /*offset=*/0);
+
+ Unsafe tempUnsafe = null;
+ Class<sun.misc.Unsafe> clazz = sun.misc.Unsafe.class;
+ for (java.lang.reflect.Field f : clazz.getDeclaredFields()) {
+ f.setAccessible(true);
+ Object obj = f.get(null);
+ if (clazz.isInstance(obj)) {
+ tempUnsafe = clazz.cast(obj);
+ }
+ }
+ if (tempUnsafe == null) {
+ throw new Exception("Couldn't get Unsafe");
+ }
+ Method setMemory = clazz.getMethod("setMemory", long.class, long.class, byte.class);
+ setMemory.invoke(tempUnsafe, address, alignedPinSize, (byte) 1);
+ Os.mlock(address, alignedPinSize);
+ mCurrentlyPinnedAnonSize = alignedPinSize;
+ mPinAnonAddress = address;
+ address = -1;
+ Slog.e(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize);
+ } catch (Exception ex) {
+ Slog.e(TAG, "Could not pin anon region of size " + alignedPinSize, ex);
+ return;
+ } finally {
+ if (address >= 0) {
+ safeMunmap(address, alignedPinSize);
+ }
+ }
+ }
+
+ private void unpinAnonRegion() {
+ if (mPinAnonAddress != 0) {
+ safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize);
+ }
+ }
+
+ /**
* @return The maximum amount of bytes to be pinned for an app of type {@code key}.
*/
private int getSizeLimitForKey(@AppKey int key) {
@@ -1083,6 +1155,9 @@
totalSize += pf.bytesPinned;
}
}
+ if (mPinAnonAddress != 0) {
+ pw.format("Pinned anon region: %s\n", mCurrentlyPinnedAnonSize);
+ }
pw.format("Total size: %s\n", totalSize);
pw.println();
if (!mPendingRepin.isEmpty()) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 6baae4b..e17424b 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -937,7 +937,7 @@
mActivity.addErrorToDropBox(
dropboxTag, null, "system_server", null, null, null,
null, report.toString(), stack, null, null, null,
- errorId);
+ errorId, null);
}
}
};
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c4816fb..5773e20 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2115,6 +2115,34 @@
mVoiceInteractionManagerProvider = provider;
}
+ /**
+ * Represents volatile states associated with a Dropbox entry.
+ * <p>
+ * These states, such as the process frozen state, can change quickly over time and thus
+ * should be captured as soon as possible to ensure accurate state. If a state is undefined,
+ * it means that the state was not read early and a fallback value can be used.
+ * </p>
+ */
+ static class VolatileDropboxEntryStates {
+ private final Boolean mIsProcessFrozen;
+
+ private VolatileDropboxEntryStates(Boolean frozenState) {
+ this.mIsProcessFrozen = frozenState;
+ }
+
+ public static VolatileDropboxEntryStates withProcessFrozenState(boolean frozenState) {
+ return new VolatileDropboxEntryStates(frozenState);
+ }
+
+ public static VolatileDropboxEntryStates emptyVolatileDropboxEnytyStates() {
+ return new VolatileDropboxEntryStates(null);
+ }
+
+ public Boolean isProcessFrozen() {
+ return mIsProcessFrozen;
+ }
+ }
+
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
@@ -8941,7 +8969,7 @@
addErrorToDropBox(
eventType, r, processName, null, null, null, null, null, null, crashInfo,
- new Float(loadingProgress), incrementalMetrics, null);
+ new Float(loadingProgress), incrementalMetrics, null, null);
// For GWP-ASan recoverable crashes, don't make the app crash (the whole point of
// 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
@@ -9052,7 +9080,7 @@
final StringBuilder sb = new StringBuilder(1024);
synchronized (sb) {
- appendDropBoxProcessHeaders(process, processName, sb);
+ appendDropBoxProcessHeaders(process, processName, null, sb);
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
sb.append("System-App: ").append(isSystemApp).append("\n");
sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
@@ -9155,7 +9183,7 @@
callingPid, (r != null) ? r.getProcessClassEnum() : 0);
addErrorToDropBox("wtf", r, processName, null, null, null, tag, null, null, crashInfo,
- null, null, null);
+ null, null, null, null);
return r;
}
@@ -9180,7 +9208,7 @@
for (Pair<String, ApplicationErrorReport.CrashInfo> p = list.poll();
p != null; p = list.poll()) {
addErrorToDropBox("wtf", proc, "system_server", null, null, null, p.first, null, null,
- p.second, null, null, null);
+ p.second, null, null, null, null);
}
}
@@ -9203,7 +9231,7 @@
* to append various headers to the dropbox log text.
*/
void appendDropBoxProcessHeaders(ProcessRecord process, String processName,
- final StringBuilder sb) {
+ final VolatileDropboxEntryStates volatileStates, final StringBuilder sb) {
// Watchdog thread ends up invoking this function (with
// a null ProcessRecord) to add the stack file to dropbox.
// Do not acquire a lock on this (am) in such cases, as it
@@ -9222,7 +9250,12 @@
sb.append("PID: ").append(process.getPid()).append("\n");
sb.append("UID: ").append(process.uid).append("\n");
if (process.mOptRecord != null) {
- sb.append("Frozen: ").append(process.mOptRecord.isFrozen()).append("\n");
+ // Use 'isProcessFrozen' from 'volatileStates' if it'snon-null (present),
+ // otherwise use 'isFrozen' from 'mOptRecord'.
+ sb.append("Frozen: ").append(
+ (volatileStates != null && volatileStates.isProcessFrozen() != null)
+ ? volatileStates.isProcessFrozen() : process.mOptRecord.isFrozen()
+ ).append("\n");
}
int flags = process.info.flags;
final IPackageManager pm = AppGlobals.getPackageManager();
@@ -9335,7 +9368,7 @@
String subject, final String report, final File dataFile,
final ApplicationErrorReport.CrashInfo crashInfo,
@Nullable Float loadingProgress, @Nullable IncrementalMetrics incrementalMetrics,
- @Nullable UUID errorId) {
+ @Nullable UUID errorId, @Nullable VolatileDropboxEntryStates volatileStates) {
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
@@ -9357,7 +9390,7 @@
if (rateLimitResult.shouldRateLimit()) return;
final StringBuilder sb = new StringBuilder(1024);
- appendDropBoxProcessHeaders(process, processName, sb);
+ appendDropBoxProcessHeaders(process, processName, volatileStates, sb);
if (process != null) {
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e7f4bf9..46e5523 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1843,7 +1843,8 @@
dropBuilder.append(catSw.toString());
FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED);
mService.addErrorToDropBox("lowmem", null, "system_server", null,
- null, null, tag.toString(), dropBuilder.toString(), null, null, null, null, null);
+ null, null, tag.toString(), dropBuilder.toString(), null, null, null, null, null,
+ null);
synchronized (mService) {
long now = SystemClock.uptimeMillis();
if (mLastMemUsageReportTime < now) {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 09df277..40b1de6 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -297,6 +297,7 @@
ArrayList<Integer> firstPids = new ArrayList<>(5);
SparseBooleanArray lastPids = new SparseBooleanArray(20);
+ ActivityManagerService.VolatileDropboxEntryStates volatileDropboxEntriyStates = null;
mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {
latencyTracker.waitingOnAMSLockStarted();
@@ -343,6 +344,9 @@
synchronized (mProcLock) {
latencyTracker.waitingOnProcLockEnded();
setNotResponding(true);
+ volatileDropboxEntriyStates =
+ ActivityManagerService.VolatileDropboxEntryStates
+ .withProcessFrozenState(mApp.mOptRecord.isFrozen());
}
// Log the ANR to the event log.
@@ -620,7 +624,8 @@
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
parentShortComponentName, parentPr, null, report.toString(), tracesFile,
- null, new Float(loadingProgress), incrementalMetrics, errorId);
+ null, new Float(loadingProgress), incrementalMetrics, errorId,
+ volatileDropboxEntriyStates);
if (mApp.getWindowProcessController().appNotResponding(info.toString(),
() -> {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 97fb0e7..3d11c68 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -5643,7 +5643,7 @@
if (logToDropbox) {
final long now = SystemClock.elapsedRealtime();
final StringBuilder sb = new StringBuilder();
- mService.appendDropBoxProcessHeaders(app, app.processName, sb);
+ mService.appendDropBoxProcessHeaders(app, app.processName, null, sb);
sb.append("Reason: " + reason).append("\n");
sb.append("Requester UID: " + requester).append("\n");
dbox.addText(DROPBOX_TAG_IMPERCEPTIBLE_KILL, sb.toString());
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index c04c279..39172b8 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -25,6 +25,7 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
@@ -33,6 +34,7 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import com.android.server.display.DisplayManagerService.Clock;
@@ -99,6 +101,7 @@
private boolean mIsHdrLayerPresent = false;
// mMaxDesiredHdrSdrRatio should only be applied when there is a valid backlight->nits mapping
private float mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+ private boolean mForceHbmChangeCallback = false;
private boolean mIsBlockedByLowPowerMode = false;
private int mWidth;
private int mHeight;
@@ -484,7 +487,8 @@
private void updateHbmMode() {
int newHbmMode = calculateHighBrightnessMode();
updateHbmStats(newHbmMode);
- if (mHbmMode != newHbmMode) {
+ if (mHbmMode != newHbmMode || mForceHbmChangeCallback) {
+ mForceHbmChangeCallback = false;
mHbmMode = newHbmMode;
mHbmChangeCallback.run();
}
@@ -600,26 +604,32 @@
public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers,
int maxW, int maxH, int flags, float maxDesiredHdrSdrRatio) {
mHandler.post(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "HBMController#onHdrInfoChanged");
mIsHdrLayerPresent = numberOfHdrLayers > 0
&& (float) (maxW * maxH) >= ((float) (mWidth * mHeight)
* mHbmData.minimumHdrPercentOfScreen);
- final float candidateDesiredHdrSdrRatio =
+ float candidateDesiredHdrSdrRatio =
mIsHdrLayerPresent && mHdrBrightnessCfg != null
? maxDesiredHdrSdrRatio
: DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
- if (candidateDesiredHdrSdrRatio >= 1.0f) {
- mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
- } else {
+ if (candidateDesiredHdrSdrRatio < 1.0f) {
Slog.w(TAG, "Ignoring invalid desired HDR/SDR Ratio: "
+ candidateDesiredHdrSdrRatio);
- mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+ candidateDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+ }
+
+ if (!BrightnessSynchronizer.floatEquals(
+ mMaxDesiredHdrSdrRatio, candidateDesiredHdrSdrRatio)) {
+ mForceHbmChangeCallback = true;
+ mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
}
// Calling the brightness update so that we can recalculate
// brightness with HDR in mind.
onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
});
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6f0a4b4..be6133b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2328,8 +2328,7 @@
mRankingHandler = rankingHandler;
mConditionProviders = conditionProviders;
mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders,
- new SysUiStatsEvent.BuilderFactory(), flagResolver,
- new ZenModeEventLogger(mPackageManagerClient));
+ flagResolver, new ZenModeEventLogger(mPackageManagerClient));
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
@@ -2390,7 +2389,6 @@
mNotificationChannelLogger,
mAppOps,
mUserProfiles,
- new SysUiStatsEvent.BuilderFactory(),
mShowReviewPermissionsNotification);
mRankingHelper = new RankingHelper(getContext(),
mRankingHandler,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 0e37f10..b132a23 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -25,7 +25,6 @@
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.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -78,6 +77,7 @@
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
@@ -169,7 +169,6 @@
* fields.
*/
private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
- private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
/**
* All user-lockable fields for a given application.
@@ -208,7 +207,6 @@
ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
NotificationChannelLogger notificationChannelLogger,
AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
- SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
boolean showReviewPermissionsNotification) {
mContext = context;
mZenModeHelper = zenHelper;
@@ -219,7 +217,6 @@
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
mUserProfiles = userProfiles;
- mStatsEventBuilderFactory = statsEventBuilderFactory;
mShowReviewPermissionsNotification = showReviewPermissionsNotification;
XML_VERSION = 4;
@@ -2190,11 +2187,7 @@
break;
}
pulledEvents++;
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
final PackagePreferences r = mPackagePreferences.valueAt(i);
- event.writeInt(r.uid);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
// collect whether this package's importance info was user-set for later, if needed
// before the migration is enabled, this will simply default to false in all cases.
@@ -2214,15 +2207,7 @@
pkgsWithPermissionsToHandle.remove(key);
}
- event.writeInt(importance);
- event.writeInt(r.visibility);
- event.writeInt(r.lockedAppFields);
-
- // optional bool user_set_importance = 5;
- event.writeBoolean(importanceIsUserSet);
-
- // optional FsiState fsi_state = 6;
final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver()
.isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI);
@@ -2232,20 +2217,23 @@
final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission,
isStickyHunFlagEnabled);
- event.writeInt(fsiState);
-
- // optional bool is_fsi_permission_user_set = 7;
final int currentPermissionFlags = mPm.getPermissionFlags(
android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg,
UserHandle.getUserHandleForUid(r.uid));
- final boolean isUserSet =
+ final boolean fsiIsUserSet =
isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags,
isStickyHunFlagEnabled);
- event.writeBoolean(isUserSet);
-
- events.add(event.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
+ /* optional int32 importance = 2 */ importance,
+ /* optional int32 visibility = 3 */ r.visibility,
+ /* optional int32 user_locked_fields = 4 */ r.lockedAppFields,
+ /* optional bool user_set_importance = 5 */ importanceIsUserSet,
+ /* optional FsiState fsi_state = 6 */ fsiState,
+ /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet));
}
}
@@ -2256,18 +2244,18 @@
break;
}
pulledEvents++;
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
- event.writeInt(p.first);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeInt(pkgPermissions.get(p).first ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
-
- // fill out the rest of the fields with default values so as not to confuse the
- // builder
- event.writeInt(DEFAULT_VISIBILITY);
- event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
- event.writeBoolean(pkgPermissions.get(p).second); // user_set_importance field
- events.add(event.build());
+ // Because all fields are required in FrameworkStatsLog.buildStatsEvent, we have
+ // to fill in default values for all the unspecified fields.
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ p.first,
+ /* optional int32 importance = 2 */ pkgPermissions.get(p).first
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE,
+ /* optional int32 visibility = 3 */ DEFAULT_VISIBILITY,
+ /* optional int32 user_locked_fields = 4 */ DEFAULT_LOCKED_APP_FIELDS,
+ /* optional bool user_set_importance = 5 */ pkgPermissions.get(p).second,
+ /* optional FsiState fsi_state = 6 */ 0,
+ /* optional bool is_fsi_permission_user_set = 7 */ false));
}
}
}
@@ -2288,20 +2276,21 @@
if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
break;
}
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
- event.writeInt(r.uid);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeString(channel.getId());
- event.writeString(channel.getName().toString());
- event.writeString(channel.getDescription());
- event.writeInt(channel.getImportance());
- event.writeInt(channel.getUserLockedFields());
- event.writeBoolean(channel.isDeleted());
- event.writeBoolean(channel.getConversationId() != null);
- event.writeBoolean(channel.isDemoted());
- event.writeBoolean(channel.isImportantConversation());
- events.add(event.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
+ /* optional string channel_id = 2 */ channel.getId(),
+ /* optional string channel_name = 3 */ channel.getName().toString(),
+ /* optional string description = 4 */ channel.getDescription(),
+ /* optional int32 importance = 5 */ channel.getImportance(),
+ /* optional int32 user_locked_fields = 6 */
+ channel.getUserLockedFields(),
+ /* optional bool is_deleted = 7 */ channel.isDeleted(),
+ /* optional bool is_conversation = 8 */
+ channel.getConversationId() != null,
+ /* optional bool is_demoted_conversation = 9 */ channel.isDemoted(),
+ /* optional bool is_important_conversation = 10 */
+ channel.isImportantConversation()));
}
}
}
@@ -2323,16 +2312,15 @@
if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
break;
}
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
- event.writeInt(r.uid);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeString(groupChannel.getId());
- event.writeString(groupChannel.getName().toString());
- event.writeString(groupChannel.getDescription());
- event.writeBoolean(groupChannel.isBlocked());
- event.writeInt(groupChannel.getUserLockedFields());
- events.add(event.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
+ /* optional string group_id = 2 */ groupChannel.getId(),
+ /* optional string group_name = 3 */ groupChannel.getName().toString(),
+ /* optional string description = 4 */ groupChannel.getDescription(),
+ /* optional bool is_blocked = 5 */ groupChannel.isBlocked(),
+ /* optional int32 user_locked_fields = 6 */
+ groupChannel.getUserLockedFields()));
}
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1f5bd3e..e490745 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,7 +21,6 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
@@ -81,6 +80,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -116,7 +116,6 @@
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
private final NotificationManager mNotificationManager;
- private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
@@ -152,7 +151,6 @@
private String[] mPriorityOnlyDndExemptPackages;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders,
- SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
SystemUiSystemPropertiesFlags.FlagResolver flagResolver,
ZenModeEventLogger zenModeEventLogger) {
mContext = context;
@@ -174,7 +172,6 @@
mFiltering = new ZenModeFiltering(mContext);
mConditions = new ZenModeConditions(this, conditionProviders);
mServiceConfig = conditionProviders.getConfig();
- mStatsEventBuilderFactory = statsEventBuilderFactory;
mFlagResolver = flagResolver;
mZenModeEventLogger = zenModeEventLogger;
}
@@ -1314,17 +1311,14 @@
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
final ZenModeConfig config = mConfigs.valueAt(i);
- SysUiStatsEvent.Builder data = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(DND_MODE_RULE)
- .writeInt(user)
- .writeBoolean(config.manualRule != null) // enabled
- .writeBoolean(config.areChannelsBypassingDnd)
- .writeInt(ROOT_CONFIG)
- .writeString("") // name, empty for root config
- .writeInt(Process.SYSTEM_UID) // system owns root config
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeByteArray(config.toZenPolicy().toProto());
- events.add(data.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
+ /* optional int32 user = 1 */ user,
+ /* optional bool enabled = 2 */ config.manualRule != null,
+ /* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd,
+ /* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG,
+ /* optional string id = 5 */ "", // empty for root config
+ /* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config
+ /* optional DNDPolicyProto policy = 7 */ config.toZenPolicy().toProto()));
if (config.manualRule != null) {
ruleToProtoLocked(user, config.manualRule, true, events);
}
@@ -1355,21 +1349,18 @@
}
SysUiStatsEvent.Builder data;
- data = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(DND_MODE_RULE)
- .writeInt(user)
- .writeBoolean(rule.enabled)
- .writeBoolean(false) // channels_bypassing unused for rules
- .writeInt(rule.zenMode)
- .writeString(id)
- .writeInt(getPackageUid(pkg, user))
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
byte[] policyProto = new byte[]{};
if (rule.zenPolicy != null) {
policyProto = rule.zenPolicy.toProto();
}
- data.writeByteArray(policyProto);
- events.add(data.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
+ /* optional int32 user = 1 */ user,
+ /* optional bool enabled = 2 */ rule.enabled,
+ /* optional bool channels_bypassing = 3 */ false, // unused for rules
+ /* optional android.stats.dnd.ZenMode zen_mode = 4 */ rule.zenMode,
+ /* optional string id = 5 */ id,
+ /* optional int32 uid = 6 */ getPackageUid(pkg, user),
+ /* optional DNDPolicyProto policy = 7 */ policyProto));
}
private int getPackageUid(String pkg, int user) {
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index 76203ac..8f7b721 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -30,6 +30,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageArchiverService;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstaller;
@@ -99,6 +100,9 @@
private final PackageInstallerService mInstallerService;
@NonNull
+ private final PackageArchiverService mPackageArchiverService;
+
+ @NonNull
private final PackageProperty mPackageProperty;
@NonNull
@@ -127,7 +131,8 @@
@Nullable ComponentName instantAppResolverSettingsComponent,
@NonNull String requiredSupplementalProcessPackage,
@Nullable String servicesExtensionPackageName,
- @Nullable String sharedSystemSharedLibraryPackageName) {
+ @Nullable String sharedSystemSharedLibraryPackageName,
+ @NonNull PackageArchiverService packageArchiverService) {
mService = service;
mContext = context;
mDexOptHelper = dexOptHelper;
@@ -143,6 +148,7 @@
mRequiredSupplementalProcessPackage = requiredSupplementalProcessPackage;
mServicesExtensionPackageName = servicesExtensionPackageName;
mSharedSystemSharedLibraryPackageName = sharedSystemSharedLibraryPackageName;
+ mPackageArchiverService = packageArchiverService;
}
protected Computer snapshot() {
@@ -616,6 +622,12 @@
@Override
@Deprecated
+ public final IPackageArchiverService getPackageArchiverService() {
+ return mPackageArchiverService;
+ }
+
+ @Override
+ @Deprecated
public final void getPackageSizeInfo(final String packageName, int userId,
final IPackageStatsObserver observer) {
throw new UnsupportedOperationException(
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/PackageArchiverService.java
similarity index 62%
rename from services/core/java/com/android/server/pm/ArchiveManager.java
rename to services/core/java/com/android/server/pm/PackageArchiverService.java
index 5435206..9c31dc9 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/PackageArchiverService.java
@@ -22,11 +22,13 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.IPackageArchiverService;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Binder;
+import android.os.ParcelableException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -47,7 +49,9 @@
* while the data directory is kept. Archived apps are included in the list of launcher apps where
* tapping them re-installs the full app.
*/
-final class ArchiveManager {
+public class PackageArchiverService extends IPackageArchiverService.Stub {
+
+ private static final String TAG = "PackageArchiver";
private final Context mContext;
private final PackageManagerService mPm;
@@ -55,36 +59,39 @@
@Nullable
private LauncherApps mLauncherApps;
- ArchiveManager(Context context, PackageManagerService mPm) {
+ public PackageArchiverService(Context context, PackageManagerService mPm) {
this.mContext = context;
this.mPm = mPm;
}
- void archiveApp(
+ @Override
+ public void requestArchive(
@NonNull String packageName,
@NonNull String callerPackageName,
- @NonNull UserHandle user,
- @NonNull IntentSender intentSender) throws PackageManager.NameNotFoundException {
+ @NonNull IntentSender intentSender,
+ @NonNull UserHandle userHandle) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(callerPackageName);
- Objects.requireNonNull(user);
Objects.requireNonNull(intentSender);
+ Objects.requireNonNull(userHandle);
Computer snapshot = mPm.snapshotComputer();
- int callingUid = Binder.getCallingUid();
- int userId = user.getIdentifier();
- String callingPackageName = snapshot.getNameForUid(callingUid);
- snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
+ int userId = userHandle.getIdentifier();
+ int binderUid = Binder.getCallingUid();
+ int providedUid = snapshot.getPackageUid(callerPackageName, 0, userId);
+ snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
"archiveApp");
- verifyCaller(callerPackageName, callingPackageName);
- PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
- verifyInstaller(packageName, ps.getInstallSource());
+ verifyCaller(providedUid, binderUid);
+ PackageStateInternal ps = getPackageState(packageName, snapshot, binderUid, userId);
+ verifyInstaller(packageName, ps);
+ // TODO(b/291569242) Verify that this list is not empty and return failure with
+ // intentsender
List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
ps.getPackageName(),
new UserHandle(userId));
- // TODO(b/291569242) Verify that this list is not empty and return failure with intentsender
+ // TODO(b/282952870) Bug: should happen after the uninstall completes successfully
storeArchiveState(ps, mainActivities, userId);
// TODO(b/278553670) Add special strings for the delete dialog
@@ -93,15 +100,25 @@
callerPackageName, DELETE_KEEP_DATA, intentSender, userId);
}
+ private static void verifyInstaller(String packageName, PackageStateInternal ps) {
+ if (ps.getInstallSource().mUpdateOwnerPackageName == null
+ && ps.getInstallSource().mInstallerPackageName == null) {
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ packageName)));
+ }
+ }
+
@NonNull
private static PackageStateInternal getPackageState(String packageName,
- Computer snapshot, int callingUid, UserHandle user)
- throws PackageManager.NameNotFoundException {
+ Computer snapshot, int callingUid, int userId) {
PackageStateInternal ps = snapshot.getPackageStateFiltered(packageName, callingUid,
- user.getIdentifier());
+ userId);
if (ps == null) {
- throw new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("Package %s not found.", packageName));
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s not found.", packageName)));
}
return ps;
}
@@ -114,8 +131,7 @@
}
private void storeArchiveState(PackageStateInternal ps,
- List<LauncherActivityInfo> mainActivities, int userId)
- throws PackageManager.NameNotFoundException {
+ List<LauncherActivityInfo> mainActivities, int userId) {
List<ArchiveActivityInfo> activityInfos = new ArrayList<>();
for (int i = 0; i < mainActivities.size(); i++) {
// TODO(b/278553670) Extract and store launcher icons
@@ -130,41 +146,34 @@
? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
synchronized (mPm.mLock) {
- getPackageSetting(ps.getPackageName(), userId).modifyUserState(userId).setArchiveState(
- new ArchiveState(activityInfos, installerPackageName));
+ PackageSetting packageSetting = getPackageSettingLocked(ps.getPackageName(), userId);
+ packageSetting
+ .modifyUserState(userId)
+ .setArchiveState(new ArchiveState(activityInfos, installerPackageName));
}
}
@NonNull
@GuardedBy("mPm.mLock")
- private PackageSetting getPackageSetting(String packageName, int userId)
- throws PackageManager.NameNotFoundException {
+ private PackageSetting getPackageSettingLocked(String packageName, int userId) {
PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ // Shouldn't happen, we already verify presence of the package in getPackageState()
if (ps == null || !ps.getUserStateOrDefault(userId).isInstalled()) {
- throw new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("Package %s not found.", packageName));
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s not found.", packageName)));
}
return ps;
}
- private static void verifyCaller(String callerPackageName, String callingPackageName) {
- if (!TextUtils.equals(callingPackageName, callerPackageName)) {
+ private static void verifyCaller(int providedUid, int binderUid) {
+ if (providedUid != binderUid) {
throw new SecurityException(
TextUtils.formatSimple(
- "The callerPackageName %s set by the caller doesn't match the "
- + "caller's own package name %s.",
- callerPackageName,
- callingPackageName));
- }
- }
-
- private static void verifyInstaller(String packageName, InstallSource installSource) {
- // TODO(b/291060290) Verify installer supports unarchiving
- if (installSource.mUpdateOwnerPackageName == null
- && installSource.mInstallerPackageName == null) {
- throw new SecurityException(
- TextUtils.formatSimple("No installer found to archive app %s.",
- packageName));
+ "The UID %s of callerPackageName set by the caller doesn't match the "
+ + "caller's actual UID %s.",
+ providedUid,
+ binderUid));
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7ccf713..823c9af 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -796,6 +796,8 @@
final PackageInstallerService mInstallerService;
+ final PackageArchiverService mArchiverService;
+
final ArtManagerService mArtManagerService;
// TODO(b/260124949): Remove these.
@@ -1624,7 +1626,8 @@
(i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
context),
- (i, pm) -> new UpdateOwnershipHelper());
+ (i, pm) -> new UpdateOwnershipHelper(),
+ (i, pm) -> new PackageArchiverService(i.getContext(), pm));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1769,6 +1772,7 @@
mFactoryTest = testParams.factoryTest;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
+ mArchiverService = testParams.archiverService;
mInstantAppRegistry = testParams.instantAppRegistry;
mChangedPackagesTracker = testParams.changedPackagesTracker;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
@@ -2348,6 +2352,7 @@
});
mInstallerService = mInjector.getPackageInstallerService();
+ mArchiverService = mInjector.getPackageArchiverService();
final ComponentName instantAppResolverComponent = getInstantAppResolver(computer);
if (instantAppResolverComponent != null) {
if (DEBUG_INSTANT) {
@@ -4608,7 +4613,7 @@
mDomainVerificationConnection, mInstallerService, mPackageProperty,
mResolveComponentName, mInstantAppResolverSettingsComponent,
mRequiredSdkSandboxPackage, mServicesExtensionPackageName,
- mSharedSystemSharedLibraryPackageName);
+ mSharedSystemSharedLibraryPackageName, mArchiverService);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 51840e7..9495279 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -127,6 +127,8 @@
mPreparingPackageParserProducer;
private final Singleton<PackageInstallerService>
mPackageInstallerServiceProducer;
+ private final Singleton<PackageArchiverService>
+ mPackageArchiverServiceProducer;
private final ProducerWithArgument<InstantAppResolverConnection, ComponentName>
mInstantAppResolverConnectionProducer;
private final Singleton<LegacyPermissionManagerInternal>
@@ -185,7 +187,8 @@
Producer<IBackupManager> iBackupManager,
Producer<SharedLibrariesImpl> sharedLibrariesProducer,
Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer,
- Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) {
+ Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer,
+ Producer<PackageArchiverService> packageArchiverServiceProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -241,6 +244,7 @@
mCrossProfileIntentFilterHelperProducer = new Singleton<>(
crossProfileIntentFilterHelperProducer);
mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer);
+ mPackageArchiverServiceProducer = new Singleton<>(packageArchiverServiceProducer);
}
/**
@@ -387,6 +391,10 @@
return mPackageInstallerServiceProducer.get(this, mPackageManager);
}
+ public PackageArchiverService getPackageArchiverService() {
+ return mPackageArchiverServiceProducer.get(this, mPackageManager);
+ }
+
public InstantAppResolverConnection getInstantAppResolverConnection(
ComponentName instantAppResolverComponent) {
return mInstantAppResolverConnectionProducer.produce(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index ca57209..b91ce4b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -60,6 +60,7 @@
public @Nullable String incidentReportApproverPackage;
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
+ public PackageArchiverService archiverService;
public InstantAppRegistry instantAppRegistry;
public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
public InstantAppResolverConnection instantAppResolverConnection;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7897195..b01a89e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -72,7 +72,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -94,6 +93,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
/**
* Manages all permissions and handles permissions related tasks.
@@ -233,11 +233,11 @@
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkPermission(packageName, permissionName,
- deviceId, userId);
+ return mPermissionManagerServiceImpl.checkPermission(
+ packageName, permissionName, userId);
}
- return checkPermissionDelegate.checkPermission(packageName, permissionName,
- deviceId, userId, mPermissionManagerServiceImpl::checkPermission);
+ return checkPermissionDelegate.checkPermission(packageName, permissionName, userId,
+ mPermissionManagerServiceImpl::checkPermission);
}
@Override
@@ -254,10 +254,10 @@
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId);
+ return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName);
}
return checkPermissionDelegate.checkUidPermission(uid, permissionName,
- deviceId, mPermissionManagerServiceImpl::checkUidPermission);
+ mPermissionManagerServiceImpl::checkUidPermission);
}
@Override
@@ -511,14 +511,14 @@
public int getPermissionFlags(String packageName, String permissionName, int deviceId,
int userId) {
return mPermissionManagerServiceImpl
- .getPermissionFlags(packageName, permissionName, deviceId, userId);
+ .getPermissionFlags(packageName, permissionName, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permissionName, int flagMask,
int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask,
- flagValues, checkAdjustPolicyFlagPermission, deviceId, userId);
+ flagValues, checkAdjustPolicyFlagPermission, userId);
}
@Override
@@ -560,15 +560,14 @@
@Override
public void grantRuntimePermission(String packageName, String permissionName, int deviceId,
int userId) {
- mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName,
- deviceId, userId);
+ mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId);
}
@Override
public void revokeRuntimePermission(String packageName, String permissionName, int deviceId,
int userId, String reason) {
mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName,
- deviceId, userId, reason);
+ userId, reason);
}
@Override
@@ -581,14 +580,14 @@
public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int deviceId, int userId) {
return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
- permissionName, deviceId, userId);
+ permissionName, userId);
}
@Override
public boolean isPermissionRevokedByPolicy(String packageName, String permissionName,
int deviceId, int userId) {
- return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName,
- permissionName, deviceId, userId);
+ return mPermissionManagerServiceImpl
+ .isPermissionRevokedByPolicy(packageName, permissionName, userId);
}
@Override
@@ -869,7 +868,6 @@
*
* @param packageName the name of the package to be checked
* @param permissionName the name of the permission to be checked
- * @param deviceId The device ID
* @param userId the user ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
@@ -878,21 +876,20 @@
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- int deviceId, @UserIdInt int userId,
- @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl);
+ @UserIdInt int userId,
+ @NonNull TriFunction<String, String, Integer, Integer> superImpl);
/**
* Check whether the given UID has been granted the specified permission.
*
* @param uid the UID to be checked
* @param permissionName the name of the permission to be checked
- * @param deviceId The device ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
* the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*/
- int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
- TriFunction<Integer, String, Integer, Integer> superImpl);
+ int checkUidPermission(int uid, @NonNull String permissionName,
+ BiFunction<Integer, String, Integer> superImpl);
/**
* @return list of delegated permissions
@@ -921,32 +918,31 @@
@Override
public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- int deviceId, int userId,
- @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl) {
+ int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
if (mDelegatedPackageName.equals(packageName)
&& isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply("com.android.shell", permissionName, deviceId, userId);
+ return superImpl.apply("com.android.shell", permissionName, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(packageName, permissionName, deviceId, userId);
+ return superImpl.apply(packageName, permissionName, userId);
}
@Override
- public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
- @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) {
+ public int checkUidPermission(int uid, @NonNull String permissionName,
+ @NonNull BiFunction<Integer, String, Integer> superImpl) {
if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(Process.SHELL_UID, permissionName, deviceId);
+ return superImpl.apply(Process.SHELL_UID, permissionName);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(uid, permissionName, deviceId);
+ return superImpl.apply(uid, permissionName);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 6764e08..4353c57 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -681,7 +681,7 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int userId) {
final int callingUid = Binder.getCallingUid();
return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
}
@@ -724,7 +724,7 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
final int callingUid = Binder.getCallingUid();
boolean overridePolicy = false;
@@ -908,12 +908,8 @@
}
}
- private int checkPermission(String pkgName, String permName, int userId) {
- return checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, userId);
- }
-
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ public int checkPermission(String pkgName, String permName, int userId) {
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
@@ -979,12 +975,8 @@
return true;
}
- private int checkUidPermission(int uid, String permName) {
- return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
- }
-
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName) {
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
@@ -1303,8 +1295,7 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- int userId) {
+ public void grantRuntimePermission(String packageName, String permName, final int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
@@ -1477,11 +1468,11 @@
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
- checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, deviceId)
+ checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
== PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
@@ -1868,7 +1859,7 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId) {
+ @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
@@ -1931,8 +1922,7 @@
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -2069,8 +2059,8 @@
continue;
}
boolean isSystemOrPolicyFixed = (getPermissionFlags(newPackage.getPackageName(),
- permInfo.name, Context.DEVICE_ID_DEFAULT, userId) & (
- FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0;
+ permInfo.name, userId) & (FLAG_PERMISSION_SYSTEM_FIXED
+ | FLAG_PERMISSION_POLICY_FIXED)) != 0;
if (isSystemOrPolicyFixed) {
continue;
}
@@ -2236,8 +2226,7 @@
for (final int userId : userIds) {
final int permissionState = checkPermission(packageName, permName,
userId);
- final int flags = getPermissionFlags(packageName, permName,
- Context.DEVICE_ID_DEFAULT, userId);
+ final int flags = getPermissionFlags(packageName, permName, userId);
final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED
| FLAG_PERMISSION_GRANTED_BY_DEFAULT
@@ -5133,7 +5122,8 @@
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName,
+ @UserIdInt int userId) {
Objects.requireNonNull(packageName, "packageName");
Preconditions.checkArgumentNonNegative(userId, "userId");
return getGrantedPermissionsInternal(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 2d824aa..128f847 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -25,6 +25,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -136,16 +137,14 @@
void removePermission(String permName);
/**
- * Gets the permission state flags associated with a permission.
+ * Gets the state flags associated with a permission.
*
* @param packageName the package name for which to get the flags
* @param permName the permission for which to get the flags
- * @param deviceId The device for which to get the flags
* @param userId the user for which to get permission flags
* @return the permission flags
*/
- int getPermissionFlags(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ int getPermissionFlags(String packageName, String permName, int userId);
/**
* Updates the flags associated with a permission by replacing the flags in the specified mask
@@ -155,11 +154,10 @@
* @param permName The permission for which to update the flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
- * @param deviceId The device for which to update the permission flags
* @param userId The user for which to update the permission flags
*/
- void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues,
- boolean checkAdjustPolicyFlagPermission, int deviceId, @UserIdInt int userId);
+ void updatePermissionFlags(String packageName, String permName, int flagMask,
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
/**
* Update the permission flags for all packages and runtime permissions of a user in order
@@ -293,13 +291,11 @@
*
* @param packageName the package to which to grant the permission
* @param permName the permission name to grant
- * @param deviceId the device for which to grant the permission
* @param userId the user for which to grant the permission
*
- * @see #revokeRuntimePermission(String, String, int, int, String)
+ * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
*/
- void grantRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ void grantRuntimePermission(String packageName, String permName, int userId);
/**
* Revoke a runtime permission that was previously granted by
@@ -314,14 +310,13 @@
*
* @param packageName the package from which to revoke the permission
* @param permName the permission name to revoke
- * @param deviceId the device for which to revoke the permission
* @param userId the user for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
- * @see #grantRuntimePermission(String, String, int, int)
+ * @see #grantRuntimePermission(String, String, android.os.UserHandle)
*/
- void revokeRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId, String reason);
+ void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason);
/**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
@@ -338,29 +333,24 @@
* does not clearly communicate to the user what would be the benefit from grating this
* permission.
*
- * @param packageName the package name
* @param permName a permission your app wants to request
- * @param deviceId the device for which to check the permission
- * @param userId the user for which to check the permission
* @return whether you can show permission rationale UI
*/
boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId);
+ @UserIdInt int userId);
/**
- * Checks whether a particular permission has been revoked for a package by policy. Typically,
+ * Checks whether a particular permissions has been revoked for a package by policy. Typically
* the device owner or the profile owner may apply such a policy. The user cannot grant policy
* revoked permissions, hence the only way for an app to get such a permission is by a policy
* change.
*
* @param packageName the name of the package you are checking against
* @param permName the name of the permission you are checking for
- * @param deviceId the device for which you are checking the permission
- * @param userId the device for which you are checking the permission
+ *
* @return whether the permission is restricted by policy
*/
- boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId);
/**
* Get set of permissions that have been split into more granular or dependent permissions.
@@ -383,25 +373,14 @@
List<SplitPermissionInfoParcelable> getSplitPermissions();
/**
- * Check whether a permission is granted or not to a package.
- *
- * @param pkgName package name
- * @param permName permission name
- * @param deviceId device ID
- * @param userId user ID
- * @return permission result {@link PackageManager.PermissionResult}
+ * TODO:theianchen add doc describing this is the old checkPermissionImpl
*/
- int checkPermission(String pkgName, String permName, int deviceId, @UserIdInt int userId);
+ int checkPermission(String pkgName, String permName, int userId);
/**
- * Check whether a permission is granted or not to an UID.
- *
- * @param uid UID
- * @param permName permission name
- * @param deviceId device ID
- * @return permission result {@link PackageManager.PermissionResult}
+ * TODO:theianchen add doc describing this is the old checkUidPermissionImpl
*/
- int checkUidPermission(int uid, String permName, int deviceId);
+ int checkUidPermission(int uid, String permName);
/**
* Get all the package names requesting app op permissions.
@@ -421,11 +400,15 @@
@UserIdInt int userId);
/**
- * Reset the runtime permission state changes for a package for all devices.
+ * Reset the runtime permission state changes for a package.
*
* TODO(zhanghai): Turn this into package change callback?
+ *
+ * @param pkg the package
+ * @param userId the user ID
*/
- void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId);
+ void resetRuntimePermissions(@NonNull AndroidPackage pkg,
+ @UserIdInt int userId);
/**
* Reset the runtime permission state changes for all packages in a user.
@@ -466,8 +449,8 @@
/**
* Get all the permissions granted to a package.
*
- * @param packageName package name
- * @param userId user ID
+ * @param packageName the name of the package
+ * @param userId the user ID
* @return the names of the granted permissions
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index dacb8c6..7f98e21 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -120,21 +120,21 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int userId) {
Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- return mService.getPermissionFlags(packageName, permName, deviceId, userId);
+ + permName + ", userId = " + userId + ")");
+ return mService.getPermissionFlags(packageName, permName, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = "
+ permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues
+ ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission
- + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ + ", userId = " + userId + ")");
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
}
@Override
@@ -182,20 +182,18 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int userId) {
Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- mService.grantRuntimePermission(packageName, permName, deviceId, userId);
+ + permName + ", userId = " + userId + ")");
+ mService.grantRuntimePermission(packageName, permName, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId
- + ", reason = " + reason + ")");
- mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ + permName + ", userId = " + userId + ", reason = " + reason + ")");
+ mService.revokeRuntimePermission(packageName, permName, userId, reason);
}
@Override
@@ -207,20 +205,17 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, int userId) {
+ int userId) {
Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName
- + ", permName = " + permName + ", deviceId = " + deviceId
- + ", userId = " + userId + ")");
- return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
- userId);
+ + ", permName = " + permName + ", userId = " + userId + ")");
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
+ + permName + ", userId = " + userId + ")");
+ return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
}
@Override
@@ -230,17 +225,16 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ public int checkPermission(String pkgName, String permName, int userId) {
Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName
- + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- return mService.checkPermission(pkgName, permName, deviceId, userId);
+ + ", userId = " + userId + ")");
+ return mService.checkPermission(pkgName, permName, userId);
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
- Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
- + ", deviceId = " + deviceId + ")");
- return mService.checkUidPermission(uid, permName, deviceId);
+ public int checkUidPermission(int uid, String permName) {
+ Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + ")");
+ return mService.checkUidPermission(uid, permName);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 35d165b..d4c6d42 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -153,10 +153,9 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId,
- @UserIdInt int userId) {
- int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
- int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
+ public int getPermissionFlags(String packageName, String permName, int userId) {
+ int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, userId);
+ int newVal = mNewImplementation.getPermissionFlags(packageName, permName, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getPermissionFlags");
@@ -166,12 +165,11 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId,
- @UserIdInt int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
}
@Override
@@ -236,17 +234,16 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId) {
- mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
+ public void grantRuntimePermission(String packageName, String permName, int userId) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId, String reason) {
- mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
- mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, userId);
}
@Override
@@ -258,11 +255,11 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId) {
- boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName,
- permName, deviceId, userId);
- boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName,
- permName, deviceId, userId);
+ int userId) {
+ boolean oldVal = mOldImplementation
+ .shouldShowRequestPermissionRationale(packageName, permName, userId);
+ boolean newVal = mNewImplementation
+ .shouldShowRequestPermissionRationale(packageName, permName, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("shouldShowRequestPermissionRationale");
@@ -271,12 +268,11 @@
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- @UserIdInt int userId) {
- boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName,
- deviceId, userId);
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ boolean oldVal = mOldImplementation
+ .isPermissionRevokedByPolicy(packageName, permName, userId);
boolean newVal = mNewImplementation.isPermissionRevokedByPolicy(packageName, permName,
- deviceId, userId);
+ userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("isPermissionRevokedByPolicy");
@@ -296,9 +292,9 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
- int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId);
- int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId);
+ public int checkPermission(String pkgName, String permName, int userId) {
+ int oldVal = mOldImplementation.checkPermission(pkgName, permName, userId);
+ int newVal = mNewImplementation.checkPermission(pkgName, permName, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkPermission");
@@ -307,9 +303,9 @@
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
- int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId);
- int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId);
+ public int checkUidPermission(int uid, String permName) {
+ int oldVal = mOldImplementation.checkUidPermission(uid, permName);
+ int newVal = mNewImplementation.checkUidPermission(uid, permName);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkUidPermission");
@@ -376,7 +372,7 @@
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName, int userId) {
Set<String> oldVal = mOldImplementation.getGrantedPermissions(packageName, userId);
Set<String> newVal = mNewImplementation.getGrantedPermissions(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index cbeede0..4e72fae 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -158,10 +158,10 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags");
try {
- return mService.getPermissionFlags(packageName, permName, deviceId, userId);
+ return mService.getPermissionFlags(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -169,12 +169,12 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags");
try {
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -253,24 +253,23 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission");
try {
- mService.grantRuntimePermission(packageName, permName, deviceId, userId);
+ mService.grantRuntimePermission(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission");
try {
- mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ mService.revokeRuntimePermission(packageName, permName, userId, reason);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -289,24 +288,22 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, int userId) {
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale");
try {
- return mService.shouldShowRequestPermissionRationale(
- packageName, permName, deviceId, userId);
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy");
try {
- return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
+ return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -324,20 +321,20 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ public int checkPermission(String pkgName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission");
try {
- return mService.checkPermission(pkgName, permName, deviceId, userId);
+ return mService.checkPermission(pkgName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission");
try {
- return mService.checkUidPermission(uid, permName, deviceId);
+ return mService.checkUidPermission(uid, permName);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
index a2177e8..0bb969f 100644
--- a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
+++ b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
@@ -17,13 +17,13 @@
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
import libcore.io.IoUtils;
@@ -82,8 +82,8 @@
}
AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
mCachedAssetManager = assets;
diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
index 1a8c1996..56d92fb 100644
--- a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
+++ b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
@@ -80,8 +80,8 @@
private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
final AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
return assets;
}
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
index 3b4b20f..fbad762 100644
--- a/services/core/java/com/android/server/power/LowPowerStandbyController.java
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -322,11 +322,26 @@
interface Clock {
/** Returns milliseconds since boot, including time spent in sleep. */
long elapsedRealtime();
+
+ /** Returns milliseconds since boot, not counting time spent in deep sleep. */
+ long uptimeMillis();
+ }
+
+ private static class RealClock implements Clock {
+ @Override
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
}
public LowPowerStandbyController(Context context, Looper looper) {
- this(context, looper, SystemClock::elapsedRealtime,
- new DeviceConfigWrapper(), () -> ActivityManager.getService(),
+ this(context, looper, new RealClock(), new DeviceConfigWrapper(),
+ () -> ActivityManager.getService(),
new File(Environment.getDataSystemDirectory(), "low_power_standby_policy.xml"));
}
@@ -572,9 +587,9 @@
@GuardedBy("mLock")
private void updateActiveLocked() {
- final long now = mClock.elapsedRealtime();
+ final long nowElapsed = mClock.elapsedRealtime();
final boolean standbyTimeoutExpired =
- (now - mLastInteractiveTimeElapsed) >= mStandbyTimeoutConfig;
+ (nowElapsed - mLastInteractiveTimeElapsed) >= mStandbyTimeoutConfig;
final boolean maintenanceMode = mIdleSinceNonInteractive && !mIsDeviceIdle;
final boolean newActive =
mForceActive || (mIsEnabled && !mIsInteractive && standbyTimeoutExpired
@@ -600,11 +615,11 @@
if (DEBUG) {
Slog.d(TAG, "onNonInteractive");
}
- final long now = mClock.elapsedRealtime();
+ final long nowElapsed = mClock.elapsedRealtime();
synchronized (mLock) {
mIsInteractive = false;
mIsDeviceIdle = false;
- mLastInteractiveTimeElapsed = now;
+ mLastInteractiveTimeElapsed = nowElapsed;
if (mStandbyTimeoutConfig > 0) {
scheduleStandbyTimeoutAlarmLocked();
@@ -630,7 +645,7 @@
@GuardedBy("mLock")
private void scheduleStandbyTimeoutAlarmLocked() {
- final long nextAlarmTime = SystemClock.elapsedRealtime() + mStandbyTimeoutConfig;
+ final long nextAlarmTime = mClock.elapsedRealtime() + mStandbyTimeoutConfig;
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
nextAlarmTime, "LowPowerStandbyController.StandbyTimeout",
mOnStandbyTimeoutExpired, mHandler);
@@ -748,9 +763,8 @@
@GuardedBy("mLock")
private void enqueueNotifyPolicyChangedLocked() {
- final long now = mClock.elapsedRealtime();
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_POLICY_CHANGED, getPolicy());
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
private void notifyPolicyChanged(LowPowerStandbyPolicy policy) {
@@ -775,9 +789,8 @@
@GuardedBy("mLock")
private void enqueueNotifyActiveChangedLocked() {
- final long now = mClock.elapsedRealtime();
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ACTIVE_CHANGED, mIsActive);
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
/** Notify other system components about the updated Low Power Standby active state */
@@ -1308,7 +1321,6 @@
@GuardedBy("mLock")
private void enqueueNotifyAllowlistChangedLocked() {
- final long now = mClock.elapsedRealtime();
final int[] allowlistUids = getAllowlistUidsLocked();
if (DEBUG) {
@@ -1317,7 +1329,7 @@
}
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ALLOWLIST_CHANGED, allowlistUids);
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
private void notifyAllowlistChanged(int[] allowlistUids) {
@@ -1334,14 +1346,12 @@
@GuardedBy("mLock")
private void enqueueNotifyStandbyPortsChangedLocked() {
- final long now = mClock.elapsedRealtime();
-
if (DEBUG) {
Slog.d(TAG, "enqueueNotifyStandbyPortsChangedLocked");
}
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_STANDBY_PORTS_CHANGED);
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
private void notifyStandbyPortsChanged() {
@@ -1448,12 +1458,11 @@
public void onForegroundStateChanged(IBinder serviceToken, String packageName,
int userId, boolean isForeground) {
try {
- final long now = mClock.elapsedRealtime();
final int uid = mContext.getPackageManager()
.getPackageUidAsUser(packageName, userId);
final Message message =
mHandler.obtainMessage(MSG_FOREGROUND_SERVICE_STATE_CHANGED, uid, 0);
- mHandler.sendMessageAtTime(message, now);
+ mHandler.sendMessageAtTime(message, mClock.uptimeMillis());
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) {
Slog.d(TAG, "onForegroundStateChanged: Unknown package: " + packageName
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 7ea9870..6a2b824 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -3079,7 +3079,7 @@
}
FrameworkStatsLog.write(FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT, eventType, inputState,
- inputType, displayName, vendorId, hdmiPort, tifSessionId);
+ inputType, vendorId, hdmiPort, tifSessionId, displayName);
}
private static final class UserState {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 28c99c7..9eec5f8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3386,7 +3386,7 @@
rootTask.moveToFront(reason, task);
// Report top activity change to tracking services and WM
- if (mRootWindowContainer.getTopResumedActivity() == this) {
+ if (mState == RESUMED && mRootWindowContainer.getTopResumedActivity() == this) {
mAtmService.setLastResumedActivityUncheckLocked(this, reason);
}
return true;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index eb15b31..6eb9ed69 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -794,6 +794,14 @@
return false;
}
+ // Try pausing the existing resumed activity in the same TaskFragment if any.
+ final TaskFragment taskFragment = r.getTaskFragment();
+ if (taskFragment != null && taskFragment.getResumedActivity() != null) {
+ if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) {
+ return false;
+ }
+ }
+
final Task task = r.getTask();
final Task rootTask = task.getRootTask();
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 788299c..6e0d98c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -733,7 +733,13 @@
if (controller == nullptr) {
ensureSpriteControllerLocked();
- controller = PointerController::create(this, mLooper, *mLocked.spriteController);
+ static const bool ENABLE_POINTER_CHOREOGRAPHER =
+ sysprop::InputProperties::enable_pointer_choreographer().value_or(false);
+
+ // Disable the functionality of the legacy PointerController if PointerChoreographer is
+ // enabled.
+ controller = PointerController::create(this, mLooper, *mLocked.spriteController,
+ /*enabled=*/!ENABLE_POINTER_CHOREOGRAPHER);
mLocked.legacyPointerController = controller;
updateInactivityTimeoutLocked();
}
@@ -745,7 +751,7 @@
std::scoped_lock _l(mLock);
ensureSpriteControllerLocked();
std::shared_ptr<PointerController> pc =
- PointerController::create(this, mLooper, *mLocked.spriteController);
+ PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true);
mLocked.pointerControllers.emplace_back(pc);
return pc;
}
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 6a349e2..17474fb 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -26,7 +26,6 @@
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.IndexedMap
import com.android.server.permission.access.permission.AppIdPermissionPolicy
-import com.android.server.permission.access.permission.DevicePermissionPolicy
import com.android.server.permission.access.util.attributeInt
import com.android.server.permission.access.util.attributeInterned
import com.android.server.permission.access.util.forEachTag
@@ -47,7 +46,6 @@
getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
}
addPolicy(AppIdPermissionPolicy())
- addPolicy(DevicePermissionPolicy())
addPolicy(AppIdAppOpPolicy())
addPolicy(PackageAppOpPolicy())
} as IndexedMap<String, IndexedMap<String, SchemePolicy>>
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 94c878a..4ec32ea 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -329,18 +329,6 @@
private typealias AppIdPermissionFlagsReference =
MutableReference<AppIdPermissionFlags, MutableAppIdPermissionFlags>
-
-typealias DevicePermissionFlags =
- IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
-typealias MutableDevicePermissionFlags =
- MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
-typealias AppIdDevicePermissionFlags =
- IntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
-typealias MutableAppIdDevicePermissionFlags =
- MutableIntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
-private typealias AppIdDevicePermissionFlagsReference =
- MutableReference<AppIdDevicePermissionFlags, MutableAppIdDevicePermissionFlags>
-
typealias AppIdAppOpModes =
IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
typealias MutableAppIdAppOpModes =
@@ -358,7 +346,6 @@
sealed class UserState(
internal val packageVersionsReference: PackageVersionsReference,
internal val appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
- internal val appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
internal val appIdAppOpModesReference: AppIdAppOpModesReference,
internal val packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -370,9 +357,6 @@
val appIdPermissionFlags: AppIdPermissionFlags
get() = appIdPermissionFlagsReference.get()
- val appIdDevicePermissionFlags: AppIdDevicePermissionFlags
- get() = appIdDevicePermissionFlagsReference.get()
-
val appIdAppOpModes: AppIdAppOpModes
get() = appIdAppOpModesReference.get()
@@ -391,7 +375,6 @@
class MutableUserState private constructor(
packageVersionsReference: PackageVersionsReference,
appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
- appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
appIdAppOpModesReference: AppIdAppOpModesReference,
packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -399,7 +382,6 @@
) : UserState(
packageVersionsReference,
appIdPermissionFlagsReference,
- appIdDevicePermissionFlagsReference,
appIdAppOpModesReference,
packageAppOpModesReference,
defaultPermissionGrantFingerprint,
@@ -408,7 +390,6 @@
constructor() : this(
PackageVersionsReference(MutableIndexedMap<String, Int>()),
AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
- AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
AppIdAppOpModesReference(MutableAppIdAppOpModes()),
PackageAppOpModesReference(MutablePackageAppOpModes()),
null,
@@ -418,7 +399,6 @@
internal constructor(userState: UserState) : this(
userState.packageVersionsReference.toImmutable(),
userState.appIdPermissionFlagsReference.toImmutable(),
- userState.appIdDevicePermissionFlagsReference.toImmutable(),
userState.appIdAppOpModesReference.toImmutable(),
userState.packageAppOpModesReference.toImmutable(),
userState.defaultPermissionGrantFingerprint,
@@ -430,9 +410,6 @@
fun mutateAppIdPermissionFlags(): MutableAppIdPermissionFlags =
appIdPermissionFlagsReference.mutate()
- fun mutateAppIdDevicePermissionFlags(): MutableAppIdDevicePermissionFlags =
- appIdDevicePermissionFlagsReference.mutate()
-
fun mutateAppIdAppOpModes(): MutableAppIdAppOpModes = appIdAppOpModesReference.mutate()
fun mutatePackageAppOpModes(): MutablePackageAppOpModes = packageAppOpModesReference.mutate()
diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt
index 1d46ca7..d1abc04 100644
--- a/services/permission/java/com/android/server/permission/access/AccessUri.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt
@@ -65,17 +65,6 @@
}
}
-data class DevicePermissionUri(
- val permissionName: String,
- val deviceId: Int
-) : AccessUri(SCHEME) {
- override fun toString(): String = "$scheme:///$permissionName/$deviceId"
-
- companion object {
- const val SCHEME = "device-permission"
- }
-}
-
data class UidUri(
val uid: Int
) : AccessUri(SCHEME) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
deleted file mode 100644
index 37a4a90..0000000
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2023 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.permission.access.permission
-
-import android.util.Slog
-import com.android.modules.utils.BinaryXmlPullParser
-import com.android.modules.utils.BinaryXmlSerializer
-import com.android.server.permission.access.AccessState
-import com.android.server.permission.access.DevicePermissionFlags
-import com.android.server.permission.access.MutableAccessState
-import com.android.server.permission.access.MutableAppIdDevicePermissionFlags
-import com.android.server.permission.access.MutableDevicePermissionFlags
-import com.android.server.permission.access.WriteMode
-import com.android.server.permission.access.immutable.IndexedMap
-import com.android.server.permission.access.immutable.MutableIndexedMap
-import com.android.server.permission.access.immutable.forEachIndexed
-import com.android.server.permission.access.immutable.forEachReversedIndexed
-import com.android.server.permission.access.immutable.set
-import com.android.server.permission.access.util.andInv
-import com.android.server.permission.access.util.attributeInt
-import com.android.server.permission.access.util.attributeInterned
-import com.android.server.permission.access.util.forEachTag
-import com.android.server.permission.access.util.getAttributeIntOrThrow
-import com.android.server.permission.access.util.getAttributeValueOrThrow
-import com.android.server.permission.access.util.hasBits
-import com.android.server.permission.access.util.tag
-import com.android.server.permission.access.util.tagName
-
-class DevicePermissionPersistence {
- fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
- when (tagName) {
- TAG_APP_ID_DEVICE_PERMISSIONS -> parseAppIdDevicePermissions(state, userId)
- else -> {}
- }
- }
-
- private fun BinaryXmlPullParser.parseAppIdDevicePermissions(
- state: MutableAccessState,
- userId: Int
- ) {
- val userState = state.mutateUserState(userId, WriteMode.NONE)!!
- val appIdDevicePermissionFlags = userState.mutateAppIdDevicePermissionFlags()
- forEachTag {
- when (tagName) {
- TAG_APP_ID -> parseAppId(appIdDevicePermissionFlags)
- else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
- }
- }
-
- appIdDevicePermissionFlags.forEachReversedIndexed { appIdIndex, appId, _ ->
- if (appId !in state.externalState.appIdPackageNames) {
- Slog.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
- appIdDevicePermissionFlags.removeAt(appIdIndex)
- userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
- }
- }
- }
-
- private fun BinaryXmlPullParser.parseAppId(
- appIdPermissionFlags: MutableAppIdDevicePermissionFlags
- ) {
- val appId = getAttributeIntOrThrow(ATTR_ID)
- val devicePermissionFlags = MutableDevicePermissionFlags()
- appIdPermissionFlags[appId] = devicePermissionFlags
- forEachTag {
- when (tagName) {
- TAG_DEVICE -> parseDevice(devicePermissionFlags)
- else -> {
- Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
- }
- }
- }
- }
-
- private fun BinaryXmlPullParser.parseDevice(
- deviceIdPermissionFlags: MutableDevicePermissionFlags
- ) {
- val deviceId = getAttributeValueOrThrow(ATTR_ID)
- val permissionFlags = MutableIndexedMap<String, Int>()
- deviceIdPermissionFlags.put(deviceId, permissionFlags)
- forEachTag {
- when (tagName) {
- TAG_PERMISSION -> parsePermission(permissionFlags)
- else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
- }
- }
- }
-
- private fun BinaryXmlPullParser.parsePermission(
- permissionFlags: MutableIndexedMap<String, Int>
- ) {
- val name = getAttributeValueOrThrow(ATTR_NAME).intern()
- val flags = getAttributeIntOrThrow(ATTR_FLAGS)
- permissionFlags[name] = flags
- }
-
- fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
- val appIdDevicePermissionFlags = state.userStates[userId]!!.appIdDevicePermissionFlags
- tag(TAG_APP_ID_DEVICE_PERMISSIONS) {
- appIdDevicePermissionFlags.forEachIndexed { _, appId, devicePermissionFlags ->
- serializeAppId(appId, devicePermissionFlags)
- }
- }
- }
-
- private fun BinaryXmlSerializer.serializeAppId(
- appId: Int,
- devicePermissionFlags: DevicePermissionFlags
- ) {
- tag(TAG_APP_ID) {
- attributeInt(ATTR_ID, appId)
- devicePermissionFlags.forEachIndexed { _, deviceId, permissionFlags ->
- serializeDevice(deviceId, permissionFlags)
- }
- }
- }
-
- private fun BinaryXmlSerializer.serializeDevice(
- deviceId: String,
- permissionFlags: IndexedMap<String, Int>
- ) {
- tag(TAG_DEVICE) {
- attributeInterned(ATTR_ID, deviceId)
- permissionFlags.forEachIndexed { _, name, flags ->
- serializePermission(name, flags)
- }
- }
- }
-
- private fun BinaryXmlSerializer.serializePermission(name: String, flags: Int) {
- tag(TAG_PERMISSION) {
- attributeInterned(ATTR_NAME, name)
- // Never serialize one-time permissions as granted.
- val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
- flags andInv PermissionFlags.RUNTIME_GRANTED
- } else {
- flags
- }
- attributeInt(ATTR_FLAGS, serializedFlags)
- }
- }
-
- companion object {
- private val LOG_TAG = DevicePermissionPersistence::class.java.simpleName
-
- private const val TAG_APP_ID_DEVICE_PERMISSIONS = "app-id-device-permissions"
- private const val TAG_APP_ID = "app-id"
- private const val TAG_DEVICE = "device"
- private const val TAG_PERMISSION = "permission"
-
- private const val ATTR_ID = "id"
- private const val ATTR_NAME = "name"
- private const val ATTR_FLAGS = "flags"
- }
-}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
deleted file mode 100644
index c0d7546..0000000
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2023 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.permission.access.permission
-
-import android.util.Slog
-import com.android.modules.utils.BinaryXmlPullParser
-import com.android.modules.utils.BinaryXmlSerializer
-import com.android.server.permission.access.AccessState
-import com.android.server.permission.access.DevicePermissionUri
-import com.android.server.permission.access.GetStateScope
-import com.android.server.permission.access.MutableAccessState
-import com.android.server.permission.access.MutateStateScope
-import com.android.server.permission.access.SchemePolicy
-import com.android.server.permission.access.UidUri
-import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
-import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
-import com.android.server.permission.access.util.andInv
-import com.android.server.pm.pkg.PackageState
-
-class DevicePermissionPolicy : SchemePolicy() {
- private val persistence = DevicePermissionPersistence()
-
- @Volatile
- private var listeners: IndexedListSet<OnDevicePermissionFlagsChangedListener> =
- MutableIndexedListSet()
- private val listenersLock = Any()
-
- override val subjectScheme: String
- get() = UidUri.SCHEME
-
- override val objectScheme: String
- get() = DevicePermissionUri.SCHEME
-
- override fun GetStateScope.onStateMutated() {
- listeners.forEachIndexed { _, it -> it.onStateMutated() }
- }
-
- override fun MutateStateScope.onAppIdRemoved(appId: Int) {
- newState.userStates.forEachIndexed { userStateIndex, _, userState ->
- if (appId in userState.appIdDevicePermissionFlags) {
- newState.mutateUserStateAt(userStateIndex)
- .mutateAppIdDevicePermissionFlags() -= appId
- }
- }
- }
-
- override fun MutateStateScope.onStorageVolumeMounted(
- volumeUuid: String?,
- packageNames: List<String>,
- isSystemUpdated: Boolean
- ) {
- packageNames.forEachIndexed { _, packageName ->
- val packageState = newState.externalState.packageStates[packageName]!!
- trimPermissionStates(packageState.appId)
- }
- }
-
- override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
- trimPermissionStates(packageState.appId)
- }
-
- override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
- if (appId in newState.externalState.appIdPackageNames) {
- trimPermissionStates(appId)
- }
- }
-
- override fun MutateStateScope.onPackageUninstalled(
- packageName: String,
- appId: Int,
- userId: Int
- ) {
- resetPermissionStates(packageName, userId)
- }
-
- private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
- // It's okay to skip resetting permissions for packages that are removed,
- // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
- val packageState = newState.externalState.packageStates[packageName] ?: return
- val androidPackage = packageState.androidPackage ?: return
- val appId = packageState.appId
- val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
- androidPackage.requestedPermissions.forEach { permissionName ->
- val isRequestedByOtherPackages = anyPackageInAppId(appId) {
- it.packageName != packageName &&
- permissionName in it.androidPackage!!.requestedPermissions
- }
- if (isRequestedByOtherPackages) {
- return@forEach
- }
- appIdPermissionFlags[appId]?.forEachIndexed { _, deviceId, _ ->
- setPermissionFlags(appId, deviceId, userId, permissionName, 0)
- }
- }
- }
-
- private fun MutateStateScope.trimPermissionStates(appId: Int) {
- val requestedPermissions = MutableIndexedSet<String>()
- forEachPackageInAppId(appId) {
- requestedPermissions += it.androidPackage!!.requestedPermissions
- }
- newState.userStates.forEachIndexed { _, userId, userState ->
- userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
- _, deviceId, permissionFlags ->
- permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
- if (permissionName !in requestedPermissions) {
- setPermissionFlags(appId, deviceId, userId, permissionName, 0)
- }
- }
- }
- }
- }
-
- private inline fun MutateStateScope.anyPackageInAppId(
- appId: Int,
- state: AccessState = newState,
- predicate: (PackageState) -> Boolean
- ): Boolean {
- val packageNames = state.externalState.appIdPackageNames[appId]!!
- return packageNames.anyIndexed { _, packageName ->
- val packageState = state.externalState.packageStates[packageName]!!
- packageState.androidPackage != null && predicate(packageState)
- }
- }
-
- private inline fun MutateStateScope.forEachPackageInAppId(
- appId: Int,
- state: AccessState = newState,
- action: (PackageState) -> Unit
- ) {
- val packageNames = state.externalState.appIdPackageNames[appId]!!
- packageNames.forEachIndexed { _, packageName ->
- val packageState = state.externalState.packageStates[packageName]!!
- if (packageState.androidPackage != null) {
- action(packageState)
- }
- }
- }
-
- override fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
- with(persistence) { this@parseUserState.parseUserState(state, userId) }
- }
-
- override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
- with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
- }
-
- fun GetStateScope.getPermissionFlags(
- appId: Int,
- deviceId: String,
- userId: Int,
- permissionName: String
- ): Int =
- state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
- ?.getWithDefault(permissionName, 0) ?: 0
-
- fun MutateStateScope.setPermissionFlags(
- appId: Int,
- deviceId: String,
- userId: Int,
- permissionName: String,
- flags: Int
- ): Boolean =
- updatePermissionFlags(
- appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
- )
-
- private fun MutateStateScope.updatePermissionFlags(
- appId: Int,
- deviceId: String,
- userId: Int,
- permissionName: String,
- flagMask: Int,
- flagValues: Int
- ): Boolean {
- if (!isDeviceAwarePermission(permissionName)) {
- Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
- return false
- }
- val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
- ?.get(deviceId).getWithDefault(permissionName, 0)
- val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
- if (oldFlags == newFlags) {
- return false
- }
- val appIdDevicePermissionFlags =
- newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
- val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
- MutableIndexedReferenceMap()
- }
- val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
- permissionFlags.putWithDefault(permissionName, newFlags, 0)
- if (permissionFlags.isEmpty()) {
- devicePermissionFlags -= deviceId
- if (devicePermissionFlags.isEmpty()) {
- appIdDevicePermissionFlags -= appId
- }
- }
- listeners.forEachIndexed { _, it ->
- it.onDevicePermissionFlagsChanged(
- appId, userId, deviceId, permissionName, oldFlags, newFlags
- )
- }
- return true
- }
-
- fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners + listener
- }
- }
-
- fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners - listener
- }
- }
-
- private fun isDeviceAwarePermission(permissionName: String): Boolean =
- DEVICE_SUPPORTED_PERMISSIONS.contains(permissionName)
-
- companion object {
- private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
-
- /**
- * These permissions are supported for virtual devices.
- */
- private val DEVICE_SUPPORTED_PERMISSIONS = indexedSetOf(
- android.Manifest.permission.CAMERA,
- android.Manifest.permission.RECORD_AUDIO
- )
- }
-
- /**
- * TODO: b/289355341 - implement listener for permission changes
- * Listener for permission flags changes.
- */
- abstract class OnDevicePermissionFlagsChangedListener {
- /**
- * Called when a permission flags change has been made to the upcoming new state.
- *
- * Implementations should keep this method fast to avoid stalling the locked state mutation,
- * and only call external code after [onStateMutated] when the new state has actually become
- * the current state visible to external code.
- */
- abstract fun onDevicePermissionFlagsChanged(
- appId: Int,
- userId: Int,
- deviceId: String,
- permissionName: String,
- oldFlags: Int,
- newFlags: Int
- )
-
- /**
- * Called when the upcoming new state has become the current state.
- *
- * Implementations should keep this method fast to avoid stalling the locked state mutation.
- */
- abstract fun onStateMutated()
- }
-}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index f0705ed..b797492 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -64,11 +64,9 @@
import com.android.server.PermissionThread
import com.android.server.ServiceThread
import com.android.server.SystemConfig
-import com.android.server.companion.virtual.VirtualDeviceManagerInternal
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.AccessState
import com.android.server.permission.access.AppOpUri
-import com.android.server.permission.access.DevicePermissionUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
@@ -112,9 +110,6 @@
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
- private val devicePolicy =
- service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
-
private val context = service.context
private lateinit var metricsLogger: MetricsLogger
private lateinit var packageManagerInternal: PackageManagerInternal
@@ -135,8 +130,6 @@
@GuardedBy("storageVolumeLock")
private val storageVolumePackageNames = ArrayMap<String?, MutableList<String>>()
- private var virtualDeviceManagerInternal: VirtualDeviceManagerInternal? = null
-
private lateinit var permissionControllerManager: PermissionControllerManager
/**
@@ -159,6 +152,7 @@
systemConfig = SystemConfig.getInstance()
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
+
// The package info cache is the cache for package and permission information.
// Disable the package info and package permission caches locally but leave the
// checkPermission cache active.
@@ -466,7 +460,7 @@
return size
}
- override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int {
+ override fun checkUidPermission(uid: Int, permissionName: String): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
@@ -488,7 +482,7 @@
return PackageManager.PERMISSION_DENIED
}
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
+ isPermissionGranted(packageState, userId, permissionName)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -521,12 +515,7 @@
return false
}
- override fun checkPermission(
- packageName: String,
- permissionName: String,
- deviceId: Int,
- userId: Int
- ): Int {
+ override fun checkPermission(packageName: String, permissionName: String, userId: Int): Int {
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
}
@@ -535,7 +524,7 @@
.use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
+ isPermissionGranted(packageState, userId, permissionName)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -553,21 +542,19 @@
private fun GetStateScope.isPermissionGranted(
packageState: PackageState,
userId: Int,
- permissionName: String,
- deviceId: Int
+ permissionName: String
): Boolean {
val appId = packageState.appId
// Note that instant apps can't have shared UIDs, so we only need to check the current
// package state.
val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
- if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) {
+ if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName)) {
return true
}
val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
if (fullerPermissionName != null &&
- isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
- ) {
+ isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName)) {
return true
}
@@ -581,10 +568,9 @@
appId: Int,
userId: Int,
isInstantApp: Boolean,
- permissionName: String,
- deviceId: Int,
+ permissionName: String
): Boolean {
- val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
if (!PermissionFlags.isPermissionGranted(flags)) {
return false
}
@@ -615,8 +601,7 @@
?: return emptySet()
return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
- if (isPermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
permissionName
} else {
null
@@ -655,26 +640,18 @@
}
}
- override fun grantRuntimePermission(
- packageName: String,
- permissionName: String,
- deviceId: Int,
- userId: Int
- ) {
- setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = true
- )
+ override fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) {
+ setRuntimePermissionGranted(packageName, userId, permissionName, isGranted = true)
}
override fun revokeRuntimePermission(
packageName: String,
permissionName: String,
- deviceId: Int,
userId: Int,
reason: String?
) {
setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
+ packageName, userId, permissionName, isGranted = false, revokeReason = reason
)
}
@@ -683,8 +660,8 @@
userId: Int
) {
setRuntimePermissionGranted(
- packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
- isGranted = false, skipKillUid = true
+ packageName, userId, Manifest.permission.POST_NOTIFICATIONS, isGranted = false,
+ skipKillUid = true
)
}
@@ -696,7 +673,6 @@
packageName: String,
userId: Int,
permissionName: String,
- deviceId: Int,
isGranted: Boolean,
skipKillUid: Boolean = false,
revokeReason: String? = null
@@ -772,7 +748,7 @@
}
setRuntimePermissionGranted(
- packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
+ packageState, userId, permissionName, isGranted, canManageRolePermission,
overridePolicyFixed, reportError = true, methodName
)
}
@@ -806,16 +782,14 @@
if (permissionState ==
PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
setRuntimePermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- isGranted = true, canManageRolePermission = false,
- overridePolicyFixed = false, reportError = false,
- "setRequestedPermissionStates"
+ packageState, userId, permissionName, isGranted = true,
+ canManageRolePermission = false, overridePolicyFixed = false,
+ reportError = false, "setRequestedPermissionStates"
)
updatePermissionFlags(
packageState.appId, userId, permissionName,
- Context.DEVICE_ID_DEFAULT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
canUpdateSystemFlags = false,
reportErrorForUnknownPermission = false,
isPermissionRequested = true, "setRequestedPermissionStates",
@@ -842,7 +816,6 @@
packageState: PackageState,
userId: Int,
permissionName: String,
- deviceId: Int,
isGranted: Boolean,
canManageRolePermission: Boolean,
overridePolicyFixed: Boolean,
@@ -898,7 +871,7 @@
}
val appId = packageState.appId
- val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
if (reportError) {
@@ -961,7 +934,7 @@
return
}
- setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
+ with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
if (permission.isRuntime) {
val action = if (isGranted) {
@@ -990,12 +963,7 @@
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
}
- override fun getPermissionFlags(
- packageName: String,
- permissionName: String,
- deviceId: Int,
- userId: Int,
- ): Int {
+ override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "getPermissionFlags: Unknown user $userId")
return 0
@@ -1026,8 +994,7 @@
}
val flags =
- getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
-
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
return PermissionFlags.toApiFlags(flags)
}
}
@@ -1035,7 +1002,6 @@
override fun isPermissionRevokedByPolicy(
packageName: String,
permissionName: String,
- deviceId: Int,
userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
@@ -1052,13 +1018,13 @@
.use { it.getPackageState(packageName) } ?: return false
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
return false
}
- val flags =
- getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
-
+ val flags = with(policy) {
+ getPermissionFlags(packageState.appId, userId, permissionName)
+ }
return flags.hasBits(PermissionFlags.POLICY_FIXED)
}
}
@@ -1080,8 +1046,7 @@
override fun shouldShowRequestPermissionRationale(
packageName: String,
permissionName: String,
- deviceId: Int,
- userId: Int,
+ userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId")
@@ -1103,11 +1068,11 @@
val flags: Int
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
return false
}
- flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
}
if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
return false
@@ -1139,7 +1104,6 @@
flagMask: Int,
flagValues: Int,
enforceAdjustPolicyPermission: Boolean,
- deviceId: Int,
userId: Int
) {
val callingUid = Binder.getCallingUid()
@@ -1235,7 +1199,7 @@
val appId = packageState.appId
service.mutateState {
updatePermissionFlags(
- appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
+ appId, userId, permissionName, flagMask, flagValues, canUpdateSystemFlags,
reportErrorForUnknownPermission = true, isPermissionRequested,
"updatePermissionFlags", packageName
)
@@ -1284,9 +1248,8 @@
val androidPackage = packageState.androidPackage ?: return@forEach
androidPackage.requestedPermissions.forEach { permissionName ->
updatePermissionFlags(
- packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- flagMask, flagValues, canUpdateSystemFlags,
- reportErrorForUnknownPermission = false,
+ packageState.appId, userId, permissionName, flagMask, flagValues,
+ canUpdateSystemFlags, reportErrorForUnknownPermission = false,
isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
)
}
@@ -1301,7 +1264,6 @@
appId: Int,
userId: Int,
permissionName: String,
- deviceId: Int,
flagMask: Int,
flagValues: Int,
canUpdateSystemFlags: Boolean,
@@ -1336,7 +1298,7 @@
return
}
- val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
@@ -1346,7 +1308,7 @@
}
val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
- setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
+ with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
}
override fun getAllowlistedRestrictedPermissions(
@@ -1403,63 +1365,6 @@
)
}
- private fun GetStateScope.getPermissionFlagsWithPolicy(
- appId: Int,
- userId: Int,
- permissionName: String,
- deviceId: Int,
- ): Int {
- return if (deviceId == Context.DEVICE_ID_DEFAULT) {
- with(policy) { getPermissionFlags(appId, userId, permissionName) }
- } else {
- val virtualDeviceManagerInternal = virtualDeviceManagerInternal
- if (virtualDeviceManagerInternal == null) {
- Slog.e(LOG_TAG, "Virtual device manager service is not available.")
- return 0
- }
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
- if (persistentDeviceId != null) {
- with(devicePolicy) {
- getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
- }
- } else {
- Slog.e(LOG_TAG, "Invalid device ID $deviceId.")
- 0
- }
- }
- }
-
- private fun MutateStateScope.setPermissionFlagsWithPolicy(
- appId: Int,
- userId: Int,
- permissionName: String,
- deviceId: Int,
- flags: Int
- ): Boolean {
- return if (deviceId == Context.DEVICE_ID_DEFAULT) {
- with(policy) {
- setPermissionFlags(appId, userId, permissionName, flags)
- }
- } else {
- val virtualDeviceManagerInternal = virtualDeviceManagerInternal
- if (virtualDeviceManagerInternal == null) {
- Slog.e(LOG_TAG, "Virtual device manager service is not available.")
- return false
- }
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
- if (persistentDeviceId != null) {
- with(devicePolicy) {
- setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
- }
- } else {
- Slog.e(LOG_TAG, "Invalid device ID $deviceId.")
- false
- }
- }
- }
-
/**
* This method does not enforce checks on the caller, should only be called after
* required checks.
@@ -1634,7 +1539,8 @@
) {
service.mutateState {
with(policy) {
- val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
+ val permissionsFlags =
+ getUidPermissionFlags(appId, userId) ?: return@mutateState
val permissions = getPermissions()
androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
@@ -1755,6 +1661,8 @@
)
}
+
+
override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
requireNotNull(permissionName) { "permissionName cannot be null" }
val packageNames = ArraySet<String>()
@@ -1971,7 +1879,7 @@
println("Permissions:")
withIndent {
userState.appIdPermissionFlags[appId]?.forEachIndexed {
- _, permissionName, flags ->
+ _, permissionName, flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
println(
"$permissionName: granted=$isGranted, flags=" +
@@ -1980,20 +1888,6 @@
}
}
- userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
- _, deviceId, devicePermissionFlags ->
- println("Permissions (Device $deviceId):")
- withIndent {
- devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
- val isGranted = PermissionFlags.isPermissionGranted(flags)
- println(
- "$permissionName: granted=$isGranted, flags=" +
- PermissionFlags.toString(flags)
- )
- }
- }
- }
-
println("App ops:")
withIndent {
userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
@@ -2109,8 +2003,6 @@
override fun onSystemReady() {
service.onSystemReady()
- virtualDeviceManagerInternal =
- LocalServices.getService(VirtualDeviceManagerInternal::class.java)
permissionControllerManager = PermissionControllerManager(
context, PermissionThread.getHandler()
)
@@ -2520,7 +2412,7 @@
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
- if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
+ if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
PackageManager.PERMISSION_GRANTED) {
return false
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt
index 5f26d6f..cd37674 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt
@@ -31,6 +31,7 @@
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.doReturn
import kotlin.test.assertFailsWith
class PackageManagerLocalSnapshotTest {
@@ -154,7 +155,7 @@
put(packageStateUser0.packageName, packageStateUser0)
put(packageStateUser10.packageName, packageStateUser10)
}
- whenever(this.packageStates) { packageStates }
+ doReturn(packageStates).whenever(this).packageStates
whenever(getPackageStateFiltered(anyString(), anyInt(), anyInt())) {
packageStates[arguments[0]]?.takeUnless {
shouldFilterApplication(it, arguments[1] as Int, arguments[2] as Int)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 55645d7..9fbf86e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -48,6 +48,7 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verifyNoMoreInteractions
@@ -351,12 +352,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
whenever(signingDetails) { SigningDetails.UNKNOWN }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 86c4335..47d9196 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -44,6 +44,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.doReturn
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.test.assertFailsWith
@@ -555,12 +556,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { pkgUserState0() }
whenever(getUserStateOrDefault(1)) { pkgUserState1() }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = pkgUserState0()
this[1] = pkgUserState1()
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index e55ff3b..98d7801 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -1084,12 +1084,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { isSystemApp }
val mockSigningDetails = SigningDetails(arrayOf(spy(Signature(signature)) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 427b5b3..4a211df 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -41,6 +41,7 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import java.util.UUID
@@ -218,12 +219,12 @@
whenever(domainSetId) { TEST_UUID }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 6bb5f39..d54d608 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -41,6 +41,8 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.doReturn
+
import java.util.UUID
class DomainVerificationUserStateOverrideTest {
@@ -155,12 +157,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 76e6ec7..8e01a11 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -32,8 +32,10 @@
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -390,6 +392,35 @@
assertEquals(Float.POSITIVE_INFINITY, hbmc.getHdrBrightnessValue(), 0.0);
}
+ @Test
+ public void testHdrRespectsChangingDesiredHdrSdrRatio() {
+ final Runnable hbmChangedCallback = mock(Runnable.class);
+ final HighBrightnessModeController hbmc = new TestHbmBuilder()
+ .setClock(new OffsettableClock())
+ .setHdrBrightnessConfig(mHdrBrightnessDeviceConfigMock)
+ .setHbmChangedCallback(hbmChangedCallback)
+ .build();
+
+ // Passthrough return the max desired hdr/sdr ratio
+ when(mHdrBrightnessDeviceConfigMock.getHdrBrightnessFromSdr(anyFloat(), anyFloat()))
+ .thenAnswer(i -> i.getArgument(1));
+
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/, 2.0f /*maxDesiredHdrSdrRatio*/);
+ advanceTime(0);
+ assertEquals(2.0f, hbmc.getHdrBrightnessValue(), EPSILON);
+ verify(hbmChangedCallback, times(1)).run();
+
+ // Verify that a change in only the desired hdrSdrRatio still results in the changed
+ // callback being invoked
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/,
+ 3.0f /*maxDesiredHdrSdrRatio*/);
+ advanceTime(0);
+ assertEquals(3.0f, hbmc.getHdrBrightnessValue(), 0.0);
+ verify(hbmChangedCallback, times(2)).run();
+ }
+
@Test
public void testHdrTrumpsSunlight() {
@@ -698,6 +729,7 @@
private class TestHbmBuilder {
OffsettableClock mClock;
HighBrightnessModeController.HdrBrightnessDeviceConfig mHdrBrightnessCfg;
+ Runnable mHdrChangedCallback = () -> {};
TestHbmBuilder setClock(OffsettableClock clock) {
mClock = clock;
@@ -711,6 +743,11 @@
return this;
}
+ TestHbmBuilder setHbmChangedCallback(Runnable runnable) {
+ mHdrChangedCallback = runnable;
+ return this;
+ }
+
HighBrightnessModeController build() {
initHandler(mClock);
if (mHighBrightnessModeMetadata == null) {
@@ -718,8 +755,8 @@
}
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
- DEFAULT_HBM_DATA, mHdrBrightnessCfg, () -> {}, mHighBrightnessModeMetadata,
- mContextSpy);
+ DEFAULT_HBM_DATA, mHdrBrightnessCfg, mHdrChangedCallback,
+ mHighBrightnessModeMetadata, mContextSpy);
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 3c75332..e578ea3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -567,9 +567,8 @@
@Throws(Exception::class)
private fun stageInstantAppResolverScan() {
- whenever(mocks.resources.getStringArray(R.array.config_ephemeralResolverPackage)) {
- arrayOf("com.android.test.ephemeral.resolver")
- }
+ doReturn(arrayOf("com.android.test.ephemeral.resolver"))
+ .whenever(mocks.resources).getStringArray(R.array.config_ephemeralResolverPackage)
stageScanNewPackage("com.android.test.ephemeral.resolver",
1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder,
withPackage = { pkg: PackageImpl ->
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
similarity index 74%
rename from services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
index a8b0a7b..c7e1bda 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
@@ -37,10 +37,9 @@
import android.content.pm.VersionedPackage;
import android.os.Binder;
import android.os.Build;
-import android.os.Process;
+import android.os.ParcelableException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -62,7 +61,7 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class ArchiveManagerTest {
+public class PackageArchiverServiceTest {
private static final String PACKAGE = "com.example";
private static final String CALLER_PACKAGE = "com.vending";
@@ -95,10 +94,11 @@
private final List<LauncherActivityInfo> mLauncherActivityInfos = createLauncherActivities();
+ private final int mUserId = UserHandle.CURRENT.getIdentifier();
+
private PackageSetting mPackageSetting;
- private PackageManagerService mPm;
- private ArchiveManager mArchiveManager;
+ private PackageArchiverService mArchiveService;
@Before
public void setUp() throws Exception {
@@ -106,7 +106,7 @@
mMockSystem.system().stageNominalSystemState();
when(mMockSystem.mocks().getInjector().getPackageInstallerService()).thenReturn(
mInstallerService);
- mPm = spy(new PackageManagerService(mMockSystem.mocks().getInjector(),
+ PackageManagerService pm = spy(new PackageManagerService(mMockSystem.mocks().getInjector(),
/* factoryTest= */false,
MockSystem.Companion.getDEFAULT_VERSION_INFO().fingerprint,
/* isEngBuild= */ false,
@@ -124,37 +124,42 @@
when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
mLauncherActivityInfos);
- doReturn(mComputer).when(mPm).snapshotComputer();
- when(mComputer.getNameForUid(eq(Binder.getCallingUid()))).thenReturn(CALLER_PACKAGE);
- mArchiveManager = new ArchiveManager(mContext, mPm);
+ doReturn(mComputer).when(pm).snapshotComputer();
+ when(mComputer.getPackageUid(eq(CALLER_PACKAGE), eq(0L), eq(mUserId))).thenReturn(
+ Binder.getCallingUid());
+ mArchiveService = new PackageArchiverService(mContext, pm);
}
@Test
public void archiveApp_callerPackageNameIncorrect() {
Exception e = assertThrows(
SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, "different", UserHandle.CURRENT,
- mIntentSender)
+ () -> mArchiveService.requestArchive(PACKAGE, "different", mIntentSender,
+ UserHandle.CURRENT
+ )
);
assertThat(e).hasMessageThat().isEqualTo(
String.format(
- "The callerPackageName %s set by the caller doesn't match the "
- + "caller's own package name %s.",
- "different",
- CALLER_PACKAGE));
+ "The UID %s of callerPackageName set by the caller doesn't match the "
+ + "caller's actual UID %s.",
+ 0,
+ Binder.getCallingUid()));
}
@Test
public void archiveApp_packageNotInstalled() {
- when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn(
+ when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
null);
Exception e = assertThrows(
- PackageManager.NameNotFoundException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT
+ )
);
- assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
}
@Test
@@ -162,11 +167,14 @@
mPackageSetting.modifyUserState(UserHandle.CURRENT.getIdentifier()).setInstalled(false);
Exception e = assertThrows(
- PackageManager.NameNotFoundException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT
+ )
);
- assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
}
@Test
@@ -183,17 +191,18 @@
when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
Exception e = assertThrows(
- SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, Process.myUserHandle(),
- mIntentSender)
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT
+ )
);
- assertThat(e).hasMessageThat().isEqualTo(
- TextUtils.formatSimple("No installer found to archive app %s.",
- PACKAGE));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("No installer found to archive app %s.", PACKAGE));
}
@Test
- public void archiveApp_success() throws PackageManager.NameNotFoundException {
+ public void archiveApp_success() {
List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
// TODO(b/278553670) Extract and store launcher icons
@@ -203,7 +212,8 @@
activityInfos.add(activityInfo);
}
- mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender);
+ mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
+
verify(mInstallerService).uninstall(
eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)),
eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender),
diff --git a/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
index d3c0e35..8e328ca 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -191,7 +191,17 @@
mTestPolicyFile = new File(mContextSpy.getCacheDir(), "lps_policy.xml");
mController = new LowPowerStandbyController(mContextSpy, mTestLooper.getLooper(),
- () -> mClock.now(), mDeviceConfigWrapperMock, () -> mIActivityManagerMock,
+ new LowPowerStandbyController.Clock() {
+ @Override
+ public long elapsedRealtime() {
+ return mClock.now();
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return mClock.now();
+ }
+ }, mDeviceConfigWrapperMock, () -> mIActivityManagerMock,
mTestPolicyFile);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index b79c7be..349a597 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -303,6 +303,7 @@
});
}
+ @FlakyTest(bugId = 297879316)
@Test
public void testStates_areMutuallyExclusive() {
forEachState(state1 -> {
@@ -523,6 +524,7 @@
});
}
+ @FlakyTest(bugId = 297879316)
@Test
public void testTwoFingersOneTap_activatedState_dispatchMotionEvents() {
goFromStateIdleTo(STATE_ACTIVATED);
@@ -583,6 +585,7 @@
returnToNormalFrom(STATE_ACTIVATED);
}
+ @FlakyTest(bugId = 297879316)
@Test
public void testFirstFingerSwipe_twoPointerDownAndActivatedState_panningState() {
goFromStateIdleTo(STATE_ACTIVATED);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index bbbab21..a0bca3b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.FlakyTest;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.InputDevice;
@@ -307,6 +308,7 @@
MagnificationScaleProvider.MAX_SCALE);
}
+ @FlakyTest(bugId = 297879435)
@Test
public void logTrackingTypingFocus_processScroll_logDuration() {
WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 4b65895..2dacda0 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -34,6 +34,7 @@
"platform-test-annotations",
"platformprotosnano",
"statsdprotolite",
+ "StatsdTestUtils",
"hamcrest-library",
"servicestests-utils",
"testables",
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 9b745f5..020afdb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -44,22 +44,11 @@
import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.os.UserHandle.USER_SYSTEM;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS;
-import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
-import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IMPORTANCE_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_CONVERSATION_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_DELETED_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_DEMOTED_CONVERSATION_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_IMPORTANT_CONVERSATION_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.UID_FIELD_NUMBER;
import static com.android.server.notification.NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER;
import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
@@ -134,6 +123,7 @@
import android.util.IntArray;
import android.util.Pair;
import android.util.StatsEvent;
+import android.util.StatsEventTestUtils;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
@@ -144,11 +134,14 @@
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.os.AtomsProto;
+import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
import com.android.os.AtomsProto.PackageNotificationPreferences;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.google.common.collect.ImmutableList;
+import com.google.protobuf.InvalidProtocolBufferException;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -218,7 +211,6 @@
private PreferencesHelper mHelper;
private AudioAttributes mAudioAttributes;
private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake();
- private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
@Before
public void setUp() throws Exception {
@@ -331,11 +323,9 @@
when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
- mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
-
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, false);
+ false);
resetZenModeHelper();
mAudioAttributes = new AudioAttributes.Builder()
@@ -683,7 +673,7 @@
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
+ /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -825,7 +815,7 @@
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
+ /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -883,7 +873,7 @@
public void testReadXml_newXml_permissionNotificationOff() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ false);
+ /* showReviewPermissionsNotification= */ false);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -941,7 +931,7 @@
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
+ /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -1521,7 +1511,7 @@
mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, false);
+ false);
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -5392,7 +5382,7 @@
}
@Test
- public void testPullPackageChannelPreferencesStats() {
+ public void testPullPackageChannelPreferencesStats() throws InvalidProtocolBufferException {
String channelId = "parent";
String name = "messages";
NotificationChannel fodderA = new NotificationChannel("a", "a", IMPORTANCE_LOW);
@@ -5406,25 +5396,40 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- int found = 0;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- ++found;
- assertEquals("uid", UID_O, builder.getValue(UID_FIELD_NUMBER));
- assertTrue("uid annotation", builder.getBooleanAnnotation(
- UID_FIELD_NUMBER, ANNOTATION_ID_IS_UID));
- assertEquals("importance", IMPORTANCE_DEFAULT, builder.getValue(
- IMPORTANCE_FIELD_NUMBER));
- assertEquals("name", name, builder.getValue(CHANNEL_NAME_FIELD_NUMBER));
- assertFalse("isconv", builder.getBoolean(IS_CONVERSATION_FIELD_NUMBER));
- assertFalse("deleted", builder.getBoolean(IS_DELETED_FIELD_NUMBER));
+ // number of channels with preferences should be 3 total
+ assertEquals("expected number of events", 3, events.size());
+ for (StatsEvent ev : events) {
+ // all of these events should be of PackageNotificationChannelPreferences type,
+ // and therefore we expect the atom to have this field.
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+
+ // uid is shared across all channels; conversation & deleted are not set in any of
+ // these channels; beyond that check individual channel properties
+ assertEquals("uid", UID_O, p.getUid());
+ assertFalse("is conversation", p.getIsConversation());
+ assertFalse("is deleted", p.getIsDeleted());
+
+ String eventChannelId = p.getChannelId();
+ if (eventChannelId.equals(channelId)) {
+ assertEquals("channel name", name, p.getChannelName());
+ assertEquals("importance", IMPORTANCE_DEFAULT, p.getImportance());
+ assertFalse("is conversation", p.getIsConversation());
+ } else if (eventChannelId.equals("a")) {
+ assertEquals("channel name", "a", p.getChannelName());
+ assertEquals("importance", IMPORTANCE_LOW, p.getImportance());
+ } else { // b
+ assertEquals("channel name", "b", p.getChannelName());
+ assertEquals("importance", IMPORTANCE_HIGH, p.getImportance());
}
}
}
@Test
- public void testPullPackageChannelPreferencesStats_one_to_one() {
+ public void testPullPackageChannelPreferencesStats_one_to_one()
+ throws InvalidProtocolBufferException {
NotificationChannel channelA = new NotificationChannel("a", "a", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_O, UID_O, channelA, true, false, UID_O, false);
NotificationChannel channelB = new NotificationChannel("b", "b", IMPORTANCE_LOW);
@@ -5437,19 +5442,22 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- int found = 0;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES) {
- Object id = builder.getValue(CHANNEL_ID_FIELD_NUMBER);
- assertTrue("missing channel in the output", channels.contains(id));
- channels.remove(id);
- }
+ assertEquals("total events", 3, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+ String id = p.getChannelId();
+ assertTrue("missing channel in the output", channels.contains(id));
+ channels.remove(id);
}
assertTrue("unexpected channel in output", channels.isEmpty());
}
@Test
- public void testPullPackageChannelPreferencesStats_conversation() {
+ public void testPullPackageChannelPreferencesStats_conversation()
+ throws InvalidProtocolBufferException {
String conversationId = "friend";
NotificationChannel parent =
@@ -5467,21 +5475,25 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- assertTrue("isConveration should be true", builder.getBoolean(
- IS_CONVERSATION_FIELD_NUMBER));
- assertFalse("not demoted", builder.getBoolean(
- IS_DEMOTED_CONVERSATION_FIELD_NUMBER));
- assertFalse("not important", builder.getBoolean(
- IS_IMPORTANT_CONVERSATION_FIELD_NUMBER));
+ // In this case, we want to check the properties of the conversation channel (not parent)
+ assertEquals("total events", 2, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+
+ if (channelId.equals(p.getChannelId())) {
+ assertTrue("isConversation should be true", p.getIsConversation());
+ assertFalse("not demoted", p.getIsDemotedConversation());
+ assertFalse("not important", p.getIsImportantConversation());
}
}
}
@Test
- public void testPullPackageChannelPreferencesStats_conversation_demoted() {
+ public void testPullPackageChannelPreferencesStats_conversation_demoted()
+ throws InvalidProtocolBufferException {
NotificationChannel parent =
new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false, UID_O, false);
@@ -5496,21 +5508,23 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- assertTrue("isConveration should be true", builder.getBoolean(
- IS_CONVERSATION_FIELD_NUMBER));
- assertTrue("is demoted", builder.getBoolean(
- IS_DEMOTED_CONVERSATION_FIELD_NUMBER));
- assertFalse("not important", builder.getBoolean(
- IS_IMPORTANT_CONVERSATION_FIELD_NUMBER));
+ assertEquals("total events", 2, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+ if (channelId.equals(p.getChannelId())) {
+ assertTrue("isConversation should be true", p.getIsConversation());
+ assertTrue("is demoted", p.getIsDemotedConversation());
+ assertFalse("not important", p.getIsImportantConversation());
}
}
}
@Test
- public void testPullPackageChannelPreferencesStats_conversation_priority() {
+ public void testPullPackageChannelPreferencesStats_conversation_priority()
+ throws InvalidProtocolBufferException {
NotificationChannel parent =
new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false, UID_O, false);
@@ -5525,21 +5539,23 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- assertTrue("isConveration should be true", builder.getBoolean(
- IS_CONVERSATION_FIELD_NUMBER));
- assertFalse("not demoted", builder.getBoolean(
- IS_DEMOTED_CONVERSATION_FIELD_NUMBER));
- assertTrue("is important", builder.getBoolean(
- IS_IMPORTANT_CONVERSATION_FIELD_NUMBER));
+ assertEquals("total events", 2, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+ if (channelId.equals(p.getChannelId())) {
+ assertTrue("isConversation should be true", p.getIsConversation());
+ assertFalse("not demoted", p.getIsDemotedConversation());
+ assertTrue("is important", p.getIsImportantConversation());
}
}
}
@Test
- public void testPullPackagePreferencesStats_postPermissionMigration() {
+ public void testPullPackagePreferencesStats_postPermissionMigration()
+ throws InvalidProtocolBufferException {
// make sure there's at least one channel for each package we want to test
NotificationChannel channelA = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channelA, true, false,
@@ -5568,23 +5584,18 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackagePreferencesStats(events, appPermissions);
- int found = 0;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
- ++found;
- int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
- boolean userSet = builder.getBoolean(
- PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
+ assertEquals("total number of packages", 3, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p = atom.getPackageNotificationPreferences();
+ int uid = p.getUid();
- // if it's one of the expected ids, then make sure the importance matches
- assertTrue(expected.containsKey(uid));
- assertThat(expected.get(uid).first).isEqualTo(
- builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
- assertThat(expected.get(uid).second).isEqualTo(userSet);
- }
+ // if it's one of the expected ids, then make sure the importance matches
+ assertTrue(expected.containsKey(uid));
+ assertThat(expected.get(uid).first).isEqualTo(p.getImportance());
+ assertThat(expected.get(uid).second).isEqualTo(p.getUserSetImportance());
}
- // should have at least one entry for each of the packages we expected to see
- assertThat(found).isAtLeast(3);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java b/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
deleted file mode 100644
index 89adc72..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.util.StatsEvent;
-
-import com.android.server.notification.SysUiStatsEvent.Builder;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Wrapper for SysUiStatsEvent that implements validation.
- */
-public class WrappedSysUiStatsEvent {
-
- static class WrappedBuilder extends Builder {
- private ArrayList<Object> mValues;
- private HashMap<Integer, HashMap<Byte, Object>> mAnnotations;
- private int mAtomId;
- private int mLastIndex;
- private boolean mBuilt;
-
- WrappedBuilder(StatsEvent.Builder builder) {
- super(builder);
- mValues = new ArrayList<>();
- mAnnotations = new HashMap<>();
- mValues.add(0); // proto fields are 1-based
- }
-
- @Override
- public Builder setAtomId(int atomId) {
- mAtomId = atomId;
- super.setAtomId(atomId);
- return this;
- }
-
- @Override
- public Builder writeInt(int value) {
- addValue(Integer.valueOf(value));
- super.writeInt(value);
- return this;
- }
-
- @Override
- public Builder addBooleanAnnotation(byte annotation, boolean value) {
- addAnnotation(annotation, Boolean.valueOf(value));
- super.addBooleanAnnotation(annotation, value);
- return this;
- }
-
- @Override
- public Builder writeString(String value) {
- addValue(value);
- super.writeString(value);
- return this;
- }
-
- @Override
- public Builder writeBoolean(boolean value) {
- addValue(Boolean.valueOf(value));
- super.writeBoolean(value);
- return this;
- }
-
- @Override
- public StatsEvent build() {
- mBuilt = true;
- return super.build();
- }
-
- public Object getValue(int index) {
- return index < mValues.size() ? mValues.get(index) : null;
- }
-
- /** useful to make assertTrue() statements more readable. */
- public boolean getBoolean(int index) {
- return (Boolean) mValues.get(index);
- }
-
- /** useful to make assertTrue() statements more readable. */
- public int getInt(int index) {
- return (Integer) mValues.get(index);
- }
-
- /** useful to make assertTrue() statements more readable. */
- public String getString(int index) {
- return (String) mValues.get(index);
- }
-
- private void addValue(Object value) {
- mLastIndex = mValues.size();
- mValues.add(value);
- }
-
- private void addAnnotation(byte annotation, Object value) {
- Integer key = Integer.valueOf(mLastIndex);
- if (!mAnnotations.containsKey(key)) {
- mAnnotations.put(key, new HashMap<>());
- }
- mAnnotations.get(key).put(Byte.valueOf(annotation), value);
- }
-
- public boolean getBooleanAnnotation(int i, byte a) {
- return ((Boolean) mAnnotations.get(Integer.valueOf(i)).get(Byte.valueOf(a)))
- .booleanValue();
- }
-
- public int getAtomId() {
- return mAtomId;
- }
- }
-
- static class WrappedBuilderFactory extends SysUiStatsEvent.BuilderFactory {
- public List<WrappedBuilder> builders;
-
- WrappedBuilderFactory() {
- builders = new ArrayList<>();
- }
-
- @Override
- Builder newBuilder() {
- WrappedBuilder b = new WrappedBuilder(StatsEvent.newBuilder());
- builders.add(b);
- return b;
- }
- }
-}
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 3ee75de..e540068 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,18 +33,12 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
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_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
-import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
-import static com.android.os.dnd.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.ENABLED_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.ID_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.UID_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
@@ -73,6 +67,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
@@ -106,6 +101,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.StatsEvent;
+import android.util.StatsEventTestUtils;
import android.util.Xml;
import com.android.internal.R;
@@ -113,12 +109,15 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.os.AtomsProto;
+import com.android.os.dnd.DNDModeProto;
import com.android.os.dnd.DNDPolicyProto;
import com.android.os.dnd.DNDProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.google.common.collect.ImmutableList;
+import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Before;
import org.junit.Test;
@@ -140,13 +139,13 @@
import java.util.Objects;
@SmallTest
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
- private static final int ZEN_MODE_FOR_TESTING = 99;
private static final String CUSTOM_PKG_NAME = "not.android";
private static final int CUSTOM_PKG_UID = 1;
private static final String CUSTOM_RULE_ID = "custom_rule";
@@ -159,7 +158,6 @@
private ZenModeHelper mZenModeHelper;
private ContentResolver mContentResolver;
@Mock AppOpsManager mAppOps;
- private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
ZenModeEventLoggerFake mZenModeEventLogger;
@@ -178,7 +176,6 @@
Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" +
e.toString());
}
- mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps);
when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
@@ -188,8 +185,7 @@
mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager);
mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(),
- mConditionProviders, mStatsEventBuilderFactory, mTestFlagResolver,
- mZenModeEventLogger);
+ mConditionProviders, mTestFlagResolver, mZenModeEventLogger);
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = new ActivityInfo();
@@ -911,37 +907,35 @@
}
@Test
- public void testProto() {
+ public void testProto() throws InvalidProtocolBufferException {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
// existence of manual rule means it should be in output
mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelper.mConfig.manualRule.pkg = "android"; // system
+ mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules
- int n = mZenModeHelper.mConfig.automaticRules.size();
- List<String> ids = new ArrayList<>(n);
- for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
- ids.add(rule.id);
- }
+ List<String> ids = new ArrayList<>();
ids.add(ZenModeConfig.MANUAL_RULE_ID);
ids.add(""); // for ROOT_CONFIG, logged with empty string as id
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
- assertEquals(n + 2, events.size()); // automatic rules + manual rule + root config
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE) {
- if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == ROOT_CONFIG) {
- assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
- assertFalse(builder.getBoolean(CHANNELS_BYPASSING_FIELD_NUMBER));
- }
- assertEquals(Process.SYSTEM_UID, builder.getInt(UID_FIELD_NUMBER));
- assertTrue(builder.getBooleanAnnotation(UID_FIELD_NUMBER, ANNOTATION_ID_IS_UID));
- String name = (String) builder.getValue(ID_FIELD_NUMBER);
- assertTrue("unexpected rule id", ids.contains(name));
- ids.remove(name);
- } else {
- fail("unexpected atom id: " + builder.getAtomId());
+ assertEquals(2, events.size()); // manual rule + root config
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ // Additional check for ID to clearly identify the root config because there's some
+ // odd behavior in the test util around enum value of 0 (the usual default, but not in
+ // this case).
+ if (cfg.getZenMode().getNumber() == ROOT_CONFIG && cfg.getId().equals("")) {
+ assertTrue(cfg.getEnabled());
+ assertFalse(cfg.getChannelsBypassing());
}
+ assertEquals(Process.SYSTEM_UID, cfg.getUid());
+ String name = cfg.getId();
+ assertTrue("unexpected rule id", ids.contains(name));
+ ids.remove(name);
}
assertEquals("extra rule in output", 0, ids.size());
}
@@ -949,28 +943,61 @@
@Test
public void testProtoWithAutoRule() throws Exception {
setupZenConfig();
- // one enabled automatic rule
- mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules(ZEN_MODE_FOR_TESTING);
+ // one enabled automatic rule. we use a non-usual zen mode value (though it has to be
+ // a real one in the enum because non-valid enum values are reverted to default).
+ mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules(ZEN_MODE_ALARMS);
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
boolean foundCustomEvent = false;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE) {
- if (ZEN_MODE_FOR_TESTING == builder.getInt(ZEN_MODE_FIELD_NUMBER)) {
- foundCustomEvent = true;
- assertEquals(CUSTOM_PKG_UID, builder.getInt(UID_FIELD_NUMBER));
- assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
- }
- } else {
- fail("unexpected atom id: " + builder.getAtomId());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if (cfg.getZenMode().getNumber() == ZEN_MODE_ALARMS) {
+ foundCustomEvent = true;
+ assertEquals(CUSTOM_PKG_UID, cfg.getUid());
+ assertTrue(cfg.getEnabled());
}
}
assertTrue("couldn't find custom rule", foundCustomEvent);
}
@Test
+ public void testProtoWithDefaultAutoRules() throws Exception {
+ setupZenConfig();
+ // clear the automatic rules so we can reset to only the default rules
+ mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
+
+ // read in XML to restore the default rules
+ ByteArrayOutputStream baos = writeXmlAndPurge(5);
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelper.pullRules(events);
+
+ // list for tracking which ids we've seen in the pulled atom output
+ List<String> ids = new ArrayList<>();
+ ids.addAll(ZenModeConfig.DEFAULT_RULE_IDS);
+ ids.add(""); // empty string for root config
+
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if (!ids.contains(cfg.getId())) {
+ fail("unexpected ID found: " + cfg.getId());
+ }
+ ids.remove(cfg.getId());
+ }
+ assertEquals("default ID(s) not found", 0, ids.size());
+ }
+
+ @Test
public void ruleUidsCached() throws Exception {
setupZenConfig();
// one enabled automatic rule
@@ -1019,10 +1046,11 @@
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
- boolean foundCustomEvent = false;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE
- && "customRule".equals(builder.getString(ID_FIELD_NUMBER))) {
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if ("customRule".equals(cfg.getId())) {
fail("non-default IDs should be redacted");
}
}
@@ -1039,14 +1067,17 @@
mZenModeHelper.pullRules(events);
boolean foundManualRule = false;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE
- && ZenModeConfig.MANUAL_RULE_ID.equals(builder.getString(ID_FIELD_NUMBER))) {
- assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if (ZenModeConfig.MANUAL_RULE_ID.equals(cfg.getId())) {
+ assertEquals(0, cfg.getUid());
foundManualRule = true;
}
}
- assertTrue("couldn't find manual rule", foundManualRule); }
+ assertTrue("couldn't find manual rule", foundManualRule);
+ }
@Test
public void testWriteXml_onlyBackupsTargetUser() throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index bb20244..b44d52e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -296,7 +296,7 @@
.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW).build();
final ActivityRecord activity1 = new ActivityBuilder(mAtm)
.setTask(task1).setUid(ActivityBuilder.DEFAULT_FAKE_UID + 1).build();
- task1.setResumedActivity(activity1, "test");
+ activity1.setState(ActivityRecord.State.RESUMED, "test");
final ActivityRecord activity2 = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
index 4473a31..c84fe08 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
@@ -123,7 +123,7 @@
controller.setContentRecordingSessionLocked(mWaitingDisplaySession, mWm);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(
mWaitingDisplaySession);
- verify(mVirtualDisplayContent).updateRecording();
+ verify(mVirtualDisplayContent, atLeastOnce()).updateRecording();
// WHEN updating the session on the same display, so no longer waiting to record.
ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(