Merge changes from topic "permz"
* changes:
Flesh out remaining CR.wrap() methods.
Apps using storage must have runtime permission.
diff --git a/api/current.txt b/api/current.txt
index 23cf2a3..db17542 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -291,6 +291,7 @@
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
+ field public static final int allowExternalStorageSandbox = 16844201; // 0x10105a9
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -34659,9 +34660,11 @@
method @NonNull public static java.io.File getRootDirectory();
method @Deprecated public static String getStorageState(java.io.File);
method public static boolean isExternalStorageEmulated();
- method public static boolean isExternalStorageEmulated(java.io.File);
+ method public static boolean isExternalStorageEmulated(@NonNull java.io.File);
method public static boolean isExternalStorageRemovable();
- method public static boolean isExternalStorageRemovable(java.io.File);
+ method public static boolean isExternalStorageRemovable(@NonNull java.io.File);
+ method public static boolean isExternalStorageSandboxed();
+ method public static boolean isExternalStorageSandboxed(@NonNull java.io.File);
field public static String DIRECTORY_ALARMS;
field public static String DIRECTORY_AUDIOBOOKS;
field public static String DIRECTORY_DCIM;
diff --git a/cmds/bmgr/Android.bp b/cmds/bmgr/Android.bp
new file mode 100644
index 0000000..b64923b
--- /dev/null
+++ b/cmds/bmgr/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2007 The Android Open Source Project
+//
+
+java_binary {
+ name: "bmgr",
+ wrapper: "bmgr",
+ srcs: ["**/*.java"],
+}
diff --git a/cmds/bmgr/Android.mk b/cmds/bmgr/Android.mk
deleted file mode 100644
index d520cf2..0000000
--- a/cmds/bmgr/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2007 The Android Open Source Project
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := bmgrlib
-LOCAL_MODULE_STEM := bmgr
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := bmgr
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := bmgr
-LOCAL_REQUIRED_MODULES := bmgrlib
-include $(BUILD_PREBUILT)
diff --git a/cmds/media/Android.bp b/cmds/media/Android.bp
new file mode 100644
index 0000000..7879c53
--- /dev/null
+++ b/cmds/media/Android.bp
@@ -0,0 +1,8 @@
+// Copyright 2013 The Android Open Source Project
+//
+
+java_binary {
+ name: "media",
+ wrapper: "media",
+ srcs: ["**/*.java"],
+}
diff --git a/cmds/media/Android.mk b/cmds/media/Android.mk
deleted file mode 100644
index b9451c5..0000000
--- a/cmds/media/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := media_cmd
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := media
-LOCAL_SRC_FILES := media
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_PREBUILT)
diff --git a/cmds/media/media b/cmds/media/media
index 5c0eb2f..8ada914 100755
--- a/cmds/media/media
+++ b/cmds/media/media
@@ -3,5 +3,5 @@
# shell.
#
base=/system
-export CLASSPATH=$base/framework/media_cmd.jar
+export CLASSPATH=$base/framework/media.jar
exec app_process $base/bin com.android.commands.media.Media "$@"
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 023371d..80b6349 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4037,7 +4037,7 @@
* continues running even if the process is killed and restarted. To remove the watch,
* use {@link #clearWatchHeapLimit()}.
*
- * <p>This API only work if the calling process has been marked as
+ * <p>This API only works if the calling process has been marked as
* {@link ApplicationInfo#FLAG_DEBUGGABLE} or this is running on a debuggable
* (userdebug or eng) build.</p>
*
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 08239a1..2441672 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2100,6 +2100,16 @@
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags, int userId) {
final boolean differentUser = (UserHandle.myUserId() != userId);
+ ApplicationInfo ai;
+ try {
+ ai = getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_SHARED_LIBRARY_FILES
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+ (userId < 0) ? UserHandle.myUserId() : userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
synchronized (mResourcesManager) {
WeakReference<LoadedApk> ref;
if (differentUser) {
@@ -2112,11 +2122,7 @@
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
- //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
- //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
- // + ": " + packageInfo.mResources.getAssets().isUpToDate());
- if (packageInfo != null && (packageInfo.mResources == null
- || packageInfo.mResources.getAssets().isUpToDate())) {
+ if (ai != null && packageInfo != null && isLoadedApkUpToDate(packageInfo, ai)) {
if (packageInfo.isSecurityViolation()
&& (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
throw new SecurityException(
@@ -2129,16 +2135,6 @@
}
}
- ApplicationInfo ai = null;
- try {
- ai = getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- (userId < 0) ? UserHandle.myUserId() : userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
if (ai != null) {
return getPackageInfo(ai, compatInfo, flags);
}
@@ -2209,37 +2205,59 @@
}
LoadedApk packageInfo = ref != null ? ref.get() : null;
- if (packageInfo == null || (packageInfo.mResources != null
- && !packageInfo.mResources.getAssets().isUpToDate())) {
- if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
+
+ boolean isUpToDate = packageInfo != null && isLoadedApkUpToDate(packageInfo, aInfo);
+
+ if (isUpToDate) {
+ return packageInfo;
+ }
+
+ if (localLOGV) {
+ Slog.v(TAG, (includeCode ? "Loading code package "
: "Loading resource-only package ") + aInfo.packageName
+ " (in " + (mBoundApplication != null
- ? mBoundApplication.processName : null)
+ ? mBoundApplication.processName : null)
+ ")");
- packageInfo =
- new LoadedApk(this, aInfo, compatInfo, baseLoader,
- securityViolation, includeCode &&
- (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
-
- if (mSystemThread && "android".equals(aInfo.packageName)) {
- packageInfo.installSystemApplicationInfo(aInfo,
- getSystemContext().mPackageInfo.getClassLoader());
- }
-
- if (differentUser) {
- // Caching not supported across users
- } else if (includeCode) {
- mPackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
- } else {
- mResourcePackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
- }
}
+
+ packageInfo =
+ new LoadedApk(this, aInfo, compatInfo, baseLoader,
+ securityViolation, includeCode
+ && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
+
+ if (mSystemThread && "android".equals(aInfo.packageName)) {
+ packageInfo.installSystemApplicationInfo(aInfo,
+ getSystemContext().mPackageInfo.getClassLoader());
+ }
+
+ if (differentUser) {
+ // Caching not supported across users
+ } else if (includeCode) {
+ mPackages.put(aInfo.packageName,
+ new WeakReference<LoadedApk>(packageInfo));
+ } else {
+ mResourcePackages.put(aInfo.packageName,
+ new WeakReference<LoadedApk>(packageInfo));
+ }
+
return packageInfo;
}
}
+ /**
+ * Compares overlay/resource directories for a LoadedApk to determine if it's up to date
+ * with the given ApplicationInfo.
+ */
+ private boolean isLoadedApkUpToDate(LoadedApk loadedApk, ApplicationInfo appInfo) {
+ Resources packageResources = loadedApk.mResources;
+ String[] overlayDirs = ArrayUtils.defeatNullable(loadedApk.getOverlayDirs());
+ String[] resourceDirs = ArrayUtils.defeatNullable(appInfo.resourceDirs);
+
+ return (packageResources == null || packageResources.getAssets().isUpToDate())
+ && overlayDirs.length == resourceDirs.length
+ && ArrayUtils.containsAll(overlayDirs, resourceDirs);
+ }
+
@UnsupportedAppUsage
ActivityThread() {
mResourcesManager = ResourcesManager.getInstance();
@@ -5434,19 +5452,25 @@
ref = mResourcePackages.get(ai.packageName);
resApk = ref != null ? ref.get() : null;
}
+
+ final String[] oldResDirs = new String[2];
+
if (apk != null) {
+ oldResDirs[0] = apk.getResDir();
final ArrayList<String> oldPaths = new ArrayList<>();
LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
apk.updateApplicationInfo(ai, oldPaths);
}
if (resApk != null) {
+ oldResDirs[1] = resApk.getResDir();
final ArrayList<String> oldPaths = new ArrayList<>();
LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
resApk.updateApplicationInfo(ai, oldPaths);
}
+
synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
+ mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
}
ApplicationPackageManager.configurationChanged();
@@ -5699,9 +5723,17 @@
}
}
}
+
+ final String[] oldResDirs = { pkgInfo.getResDir() };
+
final ArrayList<String> oldPaths = new ArrayList<>();
LoadedApk.makePaths(this, pkgInfo.getApplicationInfo(), oldPaths);
pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+
+ synchronized (mResourcesManager) {
+ // Update affected Resources objects to use new ResourcesImpl
+ mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
+ }
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 404e520..a906790 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3049,6 +3049,15 @@
}
@Override
+ public String getAttentionServicePackageName() {
+ try {
+ return mPM.getAttentionServicePackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public String getWellbeingPackageName() {
try {
return mPM.getWellbeingPackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 11000df..41a4fba 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2122,8 +2122,7 @@
}
private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
- int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
- String[] overlayDirs) {
+ int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
final String[] splitResDirs;
final ClassLoader classLoader;
try {
@@ -2135,7 +2134,7 @@
return ResourcesManager.getInstance().getResources(activityToken,
pi.getResDir(),
splitResDirs,
- overlayDirs,
+ pi.getOverlayDirs(),
pi.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfig,
@@ -2153,11 +2152,9 @@
new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
final int displayId = getDisplayId();
- // overlayDirs is retrieved directly from ApplicationInfo since ActivityThread may have
- // a LoadedApk containing Resources with stale overlays for a remote application.
- final String[] overlayDirs = application.resourceDirs;
+
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
- getDisplayAdjustments(displayId).getCompatibilityInfo(), overlayDirs));
+ getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
}
@@ -2192,7 +2189,7 @@
final int displayId = getDisplayId();
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
- getDisplayAdjustments(displayId).getCompatibilityInfo(), pi.getOverlayDirs()));
+ getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
}
@@ -2242,8 +2239,7 @@
final int displayId = getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
- overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
- mPackageInfo.getOverlayDirs()));
+ overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
return context;
}
@@ -2258,8 +2254,7 @@
final int displayId = display.getDisplayId();
context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
- null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
- mPackageInfo.getOverlayDirs()));
+ null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
context.mDisplay = display;
return context;
}
@@ -2441,7 +2436,7 @@
ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
null, null, 0, null, null);
context.setResources(createResources(null, packageInfo, null, displayId, null,
- packageInfo.getCompatibilityInfo(), packageInfo.getOverlayDirs()));
+ packageInfo.getCompatibilityInfo()));
context.updateDisplay(displayId);
return context;
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 35658fb..b93aaa2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.CompatResources;
@@ -32,6 +33,7 @@
import android.content.res.ResourcesKey;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
+import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -1136,27 +1138,46 @@
}
// TODO(adamlesinski): Make this accept more than just overlay directories.
- final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
- @Nullable final String[] newResourceDirs) {
+ final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
+ @Nullable final String[] oldPaths) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyNewResourceDirsLocked");
+ String baseCodePath = appInfo.getBaseCodePath();
+
+ final int myUid = Process.myUid();
+ String[] newSplitDirs = appInfo.uid == myUid
+ ? appInfo.splitSourceDirs
+ : appInfo.splitPublicSourceDirs;
+
+ // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
+ String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
+ String[] copiedResourceDirs = ArrayUtils.cloneOrNull(appInfo.resourceDirs);
+
final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
final int implCount = mResourceImpls.size();
for (int i = 0; i < implCount; i++) {
final ResourcesKey key = mResourceImpls.keyAt(i);
final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
- if (impl != null && (key.mResDir == null || key.mResDir.equals(baseCodePath))) {
+
+ if (impl == null) {
+ continue;
+ }
+
+ if (key.mResDir == null
+ || key.mResDir.equals(baseCodePath)
+ || ArrayUtils.contains(oldPaths, key.mResDir)) {
updatedResourceKeys.put(impl, new ResourcesKey(
- key.mResDir,
- key.mSplitResDirs,
- newResourceDirs,
+ baseCodePath,
+ copiedSplitDirs,
+ copiedResourceDirs,
key.mLibDirs,
key.mDisplayId,
key.mOverrideConfiguration,
- key.mCompatInfo));
+ key.mCompatInfo
+ ));
}
}
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index aabe59d..b8c6d87 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -44,7 +44,7 @@
STATE_DISABLED,
STATE_ENABLED,
STATE_ENABLED_STATIC,
- STATE_TARGET_UPGRADING,
+ // @Deprecated STATE_TARGET_UPGRADING,
STATE_OVERLAY_UPGRADING,
})
/** @hide */
@@ -96,7 +96,14 @@
* The target package is currently being upgraded; the state will change
* once the package installation has finished.
* @hide
+ *
+ * @deprecated No longer used. Caused invalid transitions from enabled -> upgrading -> enabled,
+ * where an update is propagated when nothing has changed. Can occur during --dont-kill
+ * installs when code and resources are hot swapped and the Activity should not be relaunched.
+ * In all other cases, the process and therefore Activity is killed, so the state loop is
+ * irrelevant.
*/
+ @Deprecated
public static final int STATE_TARGET_UPGRADING = 4;
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 068a93a..5328dda 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -678,6 +678,14 @@
*/
public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28;
+ /**
+ * Value for {@link #privateFlags}: If {@code true} this app allows
+ * shared/external storage media to be a sandboxed view that only contains
+ * files owned by the app.
+ *
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29;
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -707,7 +715,8 @@
PRIVATE_FLAG_VIRTUAL_PRELOAD,
PRIVATE_FLAG_HAS_FRAGILE_USER_DATA,
PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE,
- PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
+ PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE,
+ PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlags {}
@@ -1822,6 +1831,16 @@
return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0;
}
+ /**
+ * If {@code true} this app allows shared/external storage media to be a
+ * sandboxed view that only contains files owned by the app.
+ *
+ * @hide
+ */
+ public boolean isExternalStorageSandboxAllowed() {
+ return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0;
+ }
+
private boolean isAllowedToUseHiddenApis() {
if (isSignedWithPlatformKey()) {
return true;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c798270..fb22187 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -740,6 +740,8 @@
String getSystemTextClassifierPackageName();
+ String getAttentionServicePackageName();
+
String getWellbeingPackageName();
String getAppPredictionServicePackageName();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1e6cea3..5d6867e 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1211,6 +1211,9 @@
* Adds a session ID to the set of sessions that will be committed atomically
* when this session is committed.
*
+ * <p>If the parent is staged or has rollback enabled, all children must have
+ * the same properties.
+ *
* @param sessionId the session ID to add to this multi-package session.
*/
public void addChildSessionId(int sessionId) {
@@ -1480,6 +1483,9 @@
/**
* Request that rollbacks be enabled or disabled for the given upgrade.
*
+ * <p>If the parent session is staged or has rollback enabled, all children sessions
+ * must have the same properties.
+ *
* @param enable set to {@code true} to enable, {@code false} to disable
* @hide
*/
@@ -1607,6 +1613,9 @@
* multi-package. In that case, if any of the children sessions fail to install at reboot,
* all the other children sessions are aborted as well.
*
+ * <p>If the parent session is staged or has rollback enabled, all children sessions
+ * must have the same properties.
+ *
* {@hide}
*/
@SystemApi @TestApi
@@ -1626,6 +1635,11 @@
installFlags |= PackageManager.INSTALL_APEX;
}
+ /** @hide */
+ public boolean getEnableRollback() {
+ return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fe7fb0f..de10bb0 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1397,6 +1397,14 @@
*/
public static final int INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS = -119;
+ /**
+ * Installation failed return code: one of the child sessions does not match the parent session
+ * in respect to staged or rollback enabled parameters.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY = -120;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
@@ -6991,6 +6999,16 @@
}
/**
+ * @return attention service package name, or null if there's none.
+ *
+ * @hide
+ */
+ public String getAttentionServicePackageName() {
+ throw new UnsupportedOperationException(
+ "getAttentionServicePackageName not implemented in subclass");
+ }
+
+ /**
* @return the wellbeing app package name, or null if it's not defined by the OEM.
*
* @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 35d1eac..0a01dcd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3689,6 +3689,12 @@
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
}
+ if (sa.getBoolean(
+ R.styleable.AndroidManifestApplication_allowExternalStorageSandbox,
+ owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX;
+ }
+
ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0);
ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0);
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 49b4cb0..514015f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1263,12 +1263,19 @@
*/
@UnsupportedAppUsage
public boolean isUpToDate() {
- for (ApkAssets apkAssets : getApkAssets()) {
- if (!apkAssets.isUpToDate()) {
+ synchronized (this) {
+ if (!mOpen) {
return false;
}
+
+ for (ApkAssets apkAssets : mApkAssets) {
+ if (!apkAssets.isUpToDate()) {
+ return false;
+ }
+ }
+
+ return true;
}
- return true;
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index bad80b8..014bc24 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -39,6 +39,7 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
@@ -47,11 +48,15 @@
*/
public class SQLiteQueryBuilder {
private static final String TAG = "SQLiteQueryBuilder";
+
private static final Pattern sLimitPattern =
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
+ private static final Pattern sAggregationPattern = Pattern.compile(
+ "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL)\\((.+)\\)");
private Map<String, String> mProjectionMap = null;
private List<Pattern> mProjectionGreylist = null;
+ private boolean mProjectionAggregationAllowed = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private String mTables = "";
@@ -203,6 +208,16 @@
return mProjectionGreylist;
}
+ /** {@hide} */
+ public void setProjectionAggregationAllowed(boolean projectionAggregationAllowed) {
+ mProjectionAggregationAllowed = projectionAggregationAllowed;
+ }
+
+ /** {@hide} */
+ public boolean isProjectionAggregationAllowed() {
+ return mProjectionAggregationAllowed;
+ }
+
/**
* Sets the cursor factory to be used for the query. You can use
* one factory for all queries on a database but it is normally
@@ -842,26 +857,48 @@
return query.toString();
}
+ private static @NonNull String maybeWithOperator(@Nullable String operator,
+ @NonNull String column) {
+ if (operator != null) {
+ return operator + "(" + column + ")";
+ } else {
+ return column;
+ }
+ }
+
+ /** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private String[] computeProjection(String[] projectionIn) {
+ public String[] computeProjection(String[] projectionIn) {
if (projectionIn != null && projectionIn.length > 0) {
if (mProjectionMap != null) {
String[] projection = new String[projectionIn.length];
int length = projectionIn.length;
for (int i = 0; i < length; i++) {
+ String operator = null;
String userColumn = projectionIn[i];
String column = mProjectionMap.get(userColumn);
+ // If aggregation is allowed, extract the underlying column
+ // that may be aggregated
+ if (mProjectionAggregationAllowed) {
+ final Matcher matcher = sAggregationPattern.matcher(userColumn);
+ if (matcher.matches()) {
+ operator = matcher.group(1);
+ userColumn = matcher.group(2);
+ column = mProjectionMap.get(userColumn);
+ }
+ }
+
if (column != null) {
- projection[i] = column;
+ projection[i] = maybeWithOperator(operator, column);
continue;
}
if (!mStrict &&
( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
/* A column alias already exist */
- projection[i] = userColumn;
+ projection[i] = maybeWithOperator(operator, userColumn);
continue;
}
@@ -878,7 +915,7 @@
if (match) {
Log.w(TAG, "Allowing abusive custom column: " + userColumn);
- projection[i] = userColumn;
+ projection[i] = maybeWithOperator(operator, userColumn);
continue;
}
}
@@ -911,7 +948,8 @@
return null;
}
- private @Nullable String computeWhere(@Nullable String selection) {
+ /** {@hide} */
+ public @Nullable String computeWhere(@Nullable String selection) {
final boolean hasInternal = !TextUtils.isEmpty(mWhereClause);
final boolean hasExternal = !TextUtils.isEmpty(selection);
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 687b721..06c32c6 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -62,7 +62,7 @@
private static final String TAG = "DnsResolver";
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
private static final int MAXPACKET = 8 * 1024;
- private static final int SLEEP_TIME = 2;
+ private static final int SLEEP_TIME_MS = 2;
@IntDef(prefix = { "CLASS_" }, value = {
CLASS_IN
@@ -228,8 +228,11 @@
return;
}
- registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
- maybeAddCancellationSignal(cancellationSignal, queryfd, lock);
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
}
/**
@@ -267,9 +270,11 @@
});
return;
}
-
- registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
- maybeAddCancellationSignal(cancellationSignal, queryfd, lock);
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
}
private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback {
@@ -368,10 +373,10 @@
queryCount++;
} else v6fd = null;
- // TODO: Use device flag to controll the sleep time.
+ // TODO: Use device flag to control the sleep time.
// Avoiding gateways drop packets if queries are sent too close together
try {
- Thread.sleep(SLEEP_TIME);
+ Thread.sleep(SLEEP_TIME_MS);
} catch (InterruptedException ex) { }
if (queryIpv4) {
@@ -391,16 +396,21 @@
final InetAddressAnswerAccumulator accumulator =
new InetAddressAnswerAccumulator(queryCount, callback);
- if (queryIpv6) registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
- if (queryIpv4) registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
-
- if (cancellationSignal == null) return;
- cancellationSignal.setOnCancelListener(() -> {
- synchronized (lock) {
- if (queryIpv4) cancelQuery(v4fd);
- if (queryIpv6) cancelQuery(v6fd);
+ synchronized (lock) {
+ if (queryIpv6) {
+ registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
}
- });
+ if (queryIpv4) {
+ registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
+ }
+ if (cancellationSignal == null) return;
+ cancellationSignal.setOnCancelListener(() -> {
+ synchronized (lock) {
+ if (queryIpv4) cancelQuery(v4fd);
+ if (queryIpv6) cancelQuery(v6fd);
+ }
+ });
+ }
}
private <T> void registerFDListener(@NonNull Executor executor,
@@ -443,9 +453,8 @@
resNetworkCancel(queryfd); // Closes fd, marks it invalid.
}
- private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal,
+ private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
@NonNull FileDescriptor queryfd, @NonNull Object lock) {
- if (cancellationSignal == null) return;
cancellationSignal.setOnCancelListener(() -> {
synchronized (lock) {
cancelQuery(queryfd);
diff --git a/core/java/android/os/BatterySaverPolicyConfig.java b/core/java/android/os/BatterySaverPolicyConfig.java
index bda4e27..879ab1e 100644
--- a/core/java/android/os/BatterySaverPolicyConfig.java
+++ b/core/java/android/os/BatterySaverPolicyConfig.java
@@ -58,7 +58,8 @@
mAdvertiseIsEnabled = in.mAdvertiseIsEnabled;
mDeferFullBackup = in.mDeferFullBackup;
mDeferKeyValueBackup = in.mDeferKeyValueBackup;
- mDeviceSpecificSettings = Collections.unmodifiableMap(in.mDeviceSpecificSettings);
+ mDeviceSpecificSettings = Collections.unmodifiableMap(
+ new ArrayMap<>(in.mDeviceSpecificSettings));
mDisableAnimation = in.mDisableAnimation;
mDisableAod = in.mDisableAod;
mDisableLaunchBoost = in.mDisableLaunchBoost;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index cceb6ed..f7e927e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -20,6 +20,8 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.storage.StorageManager;
@@ -1060,7 +1062,7 @@
* @throws IllegalArgumentException if the path is not a valid storage
* device.
*/
- public static boolean isExternalStorageRemovable(File path) {
+ public static boolean isExternalStorageRemovable(@NonNull File path) {
final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
if (volume != null) {
return volume.isRemovable();
@@ -1103,7 +1105,7 @@
* @throws IllegalArgumentException if the path is not a valid storage
* device.
*/
- public static boolean isExternalStorageEmulated(File path) {
+ public static boolean isExternalStorageEmulated(@NonNull File path) {
final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
if (volume != null) {
return volume.isEmulated();
@@ -1112,6 +1114,44 @@
}
}
+ /**
+ * Returns whether the shared/external storage media at the given path is a
+ * sandboxed view that only contains files owned by the app.
+ * <p>
+ * This value may be different from the value requested by
+ * {@code allowExternalStorageSandbox} in the app's manifest, since an app
+ * may inherit its sandboxed state based on when it was first installed.
+ * <p>
+ * Sandboxed apps can continue to discover and read media belonging to other
+ * apps via {@link android.provider.MediaStore}.
+ */
+ public static boolean isExternalStorageSandboxed() {
+ final File externalDir = sCurrentUser.getExternalDirs()[0];
+ return isExternalStorageSandboxed(externalDir);
+ }
+
+ /**
+ * Returns whether the shared/external storage media at the given path is a
+ * sandboxed view that only contains files owned by the app.
+ * <p>
+ * This value may be different from the value requested by
+ * {@code allowExternalStorageSandbox} in the app's manifest, since an app
+ * may inherit its sandboxed state based on when it was first installed.
+ * <p>
+ * Sandboxed apps can continue to discover and read media belonging to other
+ * apps via {@link android.provider.MediaStore}.
+ *
+ * @throws IllegalArgumentException if the path is not a valid storage
+ * device.
+ */
+ public static boolean isExternalStorageSandboxed(@NonNull File path) {
+ final Context context = AppGlobals.getInitialApplication();
+ final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
+ return appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
+ context.getApplicationInfo().uid,
+ context.getPackageName()) != AppOpsManager.MODE_ALLOWED;
+ }
+
static File getDirectory(String variableName, String defaultPath) {
String path = System.getenv(variableName);
return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 64e2f89..7d61bf6 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -773,8 +773,10 @@
*/
public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4;
- static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE;
- static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+ /** @hide */
+ public static final int MIN_LOCATION_MODE = LOCATION_MODE_NO_CHANGE;
+ /** @hide */
+ public static final int MAX_LOCATION_MODE = LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
/**
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1cab250..5c2eacc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14726,7 +14726,6 @@
*
* @hide
*/
- // TODO(b/117663715): require a new write permission restricted to a single source
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
static void resetToDefaults(@NonNull ContentResolver resolver, @ResetMode int resetMode,
@Nullable String prefix) {
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 610f7ed..715181f 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -512,8 +512,8 @@
* @hide
*/
public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
- if (isBoundsEmpty()
- || insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
+ if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0
+ || isBoundsEmpty()) {
return this;
}
@@ -534,6 +534,12 @@
safeInsets.right = atLeastZero(safeInsets.right - insetRight);
}
+ // If we are not cutting off part of the cutout by insetting it on bottom/right, and we also
+ // don't move it around, we can avoid the allocation and copy of the instance.
+ if (insetLeft == 0 && insetTop == 0 && mSafeInsets.equals(safeInsets)) {
+ return this;
+ }
+
Rect[] bounds = mBounds.getRects();
for (int i = 0; i < bounds.length; ++i) {
if (!bounds[i].equals(ZERO_RECT)) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5df990c..24f4c14 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7955,6 +7955,8 @@
* View is not a pane.
*
* {@see AccessibilityNodeInfo#setPaneTitle(CharSequence)}
+ *
+ * @attr ref android.R.styleable#View_accessibilityPaneTitle
*/
public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) {
if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
@@ -7970,6 +7972,8 @@
* @return The current pane title.
*
* {@see #setAccessibilityPaneTitle}.
+ *
+ * @attr ref android.R.styleable#View_accessibilityPaneTitle
*/
@InspectableProperty
@Nullable
@@ -12112,6 +12116,8 @@
* @see #setScreenReaderFocusable(boolean)
*
* @return Whether the view should be treated as a focusable unit by screen reader.
+ *
+ * @attr ref android.R.styleable#View_screenReaderFocusable
*/
@InspectableProperty
public boolean isScreenReaderFocusable() {
@@ -12130,6 +12136,8 @@
*
* @param screenReaderFocusable Whether the view should be treated as a unit by screen reader
* accessibility tools.
+ *
+ * @attr ref android.R.styleable#View_screenReaderFocusable
*/
public void setScreenReaderFocusable(boolean screenReaderFocusable) {
updatePflags3AndNotifyA11yIfChanged(PFLAG3_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
@@ -17960,7 +17968,7 @@
final int scrollX = mScrollX;
final int scrollY = mScrollY;
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
- dirty.right - scrollX, dirty.bottom - scrollY, true, false);
+ dirty.right - scrollX, dirty.bottom - scrollY, true);
}
/**
@@ -17986,7 +17994,7 @@
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
- invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
+ invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true);
}
/**
@@ -18016,11 +18024,10 @@
*/
@UnsupportedAppUsage
public void invalidate(boolean invalidateCache) {
- invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
+ invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache);
}
- void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
- boolean fullInvalidate) {
+ void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
@@ -18037,11 +18044,9 @@
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
- || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
- if (fullInvalidate) {
- mLastIsOpaque = isOpaque();
- mPrivateFlags &= ~PFLAG_DRAWN;
- }
+ || isOpaque() != mLastIsOpaque) {
+ mLastIsOpaque = isOpaque();
+ mPrivateFlags &= ~PFLAG_DRAWN;
mPrivateFlags |= PFLAG_DIRTY;
@@ -22586,12 +22591,7 @@
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
if (verifyDrawable(drawable)) {
- final Rect dirty = drawable.getDirtyBounds();
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
-
- invalidate(dirty.left + scrollX, dirty.top + scrollY,
- dirty.right + scrollX, dirty.bottom + scrollY);
+ invalidate();
rebuildOutline();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5f60333..49166ad 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1455,6 +1455,7 @@
@Override
public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
+ checkThread();
if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
mIsAnimating = true;
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f29174b..f14b50d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1594,7 +1594,7 @@
if (info == null) return null;
// Now fetch app icon and raster with no badging even in work profile
- Bitmap appIcon = (new ActivityInfoPresentationGetter(info)).getIconBitmap();
+ Bitmap appIcon = makePresentationGetter(info).getIconBitmap();
// Raster target drawable with appIcon as a badge
SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this);
@@ -1865,8 +1865,9 @@
ri.noResourceId = true;
ri.icon = 0;
}
+ ResolveInfoPresentationGetter getter = makePresentationGetter(ri);
mCallerTargets.add(new DisplayResolveInfo(ii, ri,
- ri.loadLabel(pm), null, ii));
+ getter.getLabel(), getter.getSubLabel(), ii));
}
}
}
@@ -1879,12 +1880,6 @@
}
@Override
- public boolean showsExtendedInfo(TargetInfo info) {
- // We have badges so we don't need this text shown.
- return false;
- }
-
- @Override
public View onCreateView(ViewGroup parent) {
return mInflater.inflate(
com.android.internal.R.layout.resolve_grid_item, parent, false);
@@ -2301,8 +2296,10 @@
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
int columnCount = holder.getColumnCount();
+ final boolean isDirectShare = holder instanceof DirectShareViewHolder;
+
for (int i = 0; i < columnCount; i++) {
- final View v = mChooserListAdapter.createView(holder.getRow(i));
+ final View v = mChooserListAdapter.createView(holder.getRowByIndex(i));
final int column = i;
v.setOnClickListener(new OnClickListener() {
@Override
@@ -2321,27 +2318,31 @@
});
ViewGroup row = holder.addView(i, v);
- // Force height to be a given so we don't have visual disruption during scaling.
- LayoutParams lp = v.getLayoutParams();
- v.measure(spec, spec);
- if (lp == null) {
- lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
- row.setLayoutParams(lp);
- } else {
- lp.height = v.getMeasuredHeight();
+ // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
+ // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
+ // done before measuring.
+ if (isDirectShare) {
+ final ViewHolder vh = (ViewHolder) v.getTag();
+ vh.text.setLines(2);
+ vh.text.setHorizontallyScrolling(false);
+ vh.text2.setVisibility(View.GONE);
}
+
+ // Force height to be a given so we don't have visual disruption during scaling.
+ v.measure(spec, spec);
+ setViewHeight(v, v.getMeasuredHeight());
}
final ViewGroup viewGroup = holder.getViewGroup();
- // Pre-measure so we can scale later.
+ // Pre-measure and fix height so we can scale later.
holder.measure();
- LayoutParams lp = viewGroup.getLayoutParams();
- if (lp == null) {
- lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight());
- viewGroup.setLayoutParams(lp);
- } else {
- lp.height = holder.getMeasuredRowHeight();
+ setViewHeight(viewGroup, holder.getMeasuredRowHeight());
+
+ if (isDirectShare) {
+ DirectShareViewHolder dsvh = (DirectShareViewHolder) holder;
+ setViewHeight(dsvh.getRow(0), holder.getMeasuredRowHeight());
+ setViewHeight(dsvh.getRow(1), holder.getMeasuredRowHeight());
}
viewGroup.setTag(holder);
@@ -2349,6 +2350,16 @@
return holder;
}
+ private void setViewHeight(View view, int heightPx) {
+ LayoutParams lp = view.getLayoutParams();
+ if (lp == null) {
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, heightPx);
+ view.setLayoutParams(lp);
+ } else {
+ lp.height = heightPx;
+ }
+ }
+
RowViewHolder createViewHolder(int viewType, ViewGroup parent) {
if (viewType == VIEW_TYPE_DIRECT_SHARE) {
ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
@@ -2386,7 +2397,7 @@
if (startType != lastStartType || rowPosition == getContentPreviewRowCount()) {
row.setBackground(mChooserRowLayer);
- setVertPadding(row, mChooserRowServiceSpacing, 0);
+ setVertPadding(row, 0, 0);
} else {
row.setBackground(null);
setVertPadding(row, 0, 0);
@@ -2483,7 +2494,9 @@
abstract ViewGroup getViewGroup();
- abstract ViewGroup getRow(int index);
+ abstract ViewGroup getRowByIndex(int index);
+
+ abstract ViewGroup getRow(int rowNumber);
abstract void setViewVisibility(int i, int visibility);
@@ -2532,10 +2545,15 @@
return mRow;
}
- public ViewGroup getRow(int index) {
+ public ViewGroup getRowByIndex(int index) {
return mRow;
}
+ public ViewGroup getRow(int rowNumber) {
+ if (rowNumber == 0) return mRow;
+ return null;
+ }
+
public ViewGroup addView(int index, View v) {
mRow.addView(v);
mCells[index] = v;
@@ -2574,7 +2592,7 @@
}
public ViewGroup addView(int index, View v) {
- ViewGroup row = getRow(index);
+ ViewGroup row = getRowByIndex(index);
row.addView(v);
mCells[index] = v;
@@ -2589,10 +2607,14 @@
return mParent;
}
- public ViewGroup getRow(int index) {
+ public ViewGroup getRowByIndex(int index) {
return mRows.get(index / mCellCountPerRow);
}
+ public ViewGroup getRow(int rowNumber) {
+ return mRows.get(rowNumber);
+ }
+
public void measure() {
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
getRow(0).measure(spec, spec);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 84a1bed..9f9e083 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -83,7 +83,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -499,33 +498,40 @@
/**
- * Loads the icon for the provided ApplicationInfo. Defaults to using the application icon over
- * any IntentFilter or Activity icon to increase user understanding, with an exception for
- * applications that hold the right permission. Always attempts to use icon resources over
- * PackageManager loading mechanisms so badging can be done by iconloader.
+ * Loads the icon and label for the provided ApplicationInfo. Defaults to using the application
+ * icon and label over any IntentFilter or Activity icon to increase user understanding, with an
+ * exception for applications that hold the right permission. Always attempts to use available
+ * resources over PackageManager loading mechanisms so badging can be done by iconloader. Uses
+ * Strings to strip creative formatting.
*/
- private abstract class TargetPresentationGetter {
- @Nullable abstract Drawable getIconSubstitute();
- @Nullable abstract String getAppSubLabel();
+ private abstract static class TargetPresentationGetter {
+ @Nullable abstract Drawable getIconSubstituteInternal();
+ @Nullable abstract String getAppSubLabelInternal();
+ private Context mCtx;
+ protected PackageManager mPm;
private final ApplicationInfo mAi;
+ private final int mIconDpi;
private final boolean mHasSubstitutePermission;
- TargetPresentationGetter(ApplicationInfo ai) {
+ TargetPresentationGetter(Context ctx, int iconDpi, ApplicationInfo ai) {
+ mCtx = ctx;
+ mPm = ctx.getPackageManager();
mAi = ai;
+ mIconDpi = iconDpi;
mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission(
android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON,
mAi.packageName);
}
- Drawable getIcon() {
- return new BitmapDrawable(getResources(), getIconBitmap());
+ public Drawable getIcon() {
+ return new BitmapDrawable(mCtx.getResources(), getIconBitmap());
}
- Bitmap getIconBitmap() {
+ public Bitmap getIconBitmap() {
Drawable dr = null;
if (mHasSubstitutePermission) {
- dr = getIconSubstitute();
+ dr = getIconSubstituteInternal();
}
if (dr == null) {
@@ -542,18 +548,18 @@
dr = mAi.loadIcon(mPm);
}
- SimpleIconFactory sif = SimpleIconFactory.obtain(ResolverActivity.this);
+ SimpleIconFactory sif = SimpleIconFactory.obtain(mCtx);
Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle());
sif.recycle();
return icon;
}
- String getLabel() {
+ public String getLabel() {
String label = null;
// Apps with the substitute permission will always show the sublabel as their label
if (mHasSubstitutePermission) {
- label = getAppSubLabel();
+ label = getAppSubLabelInternal();
}
if (label == null) {
@@ -563,10 +569,14 @@
return label;
}
- String getSubLabel() {
+ public String getSubLabel() {
// Apps with the substitute permission will never have a sublabel
if (mHasSubstitutePermission) return null;
- return getAppSubLabel();
+ return getAppSubLabelInternal();
+ }
+
+ protected String loadLabelFromResource(Resources res, int resId) {
+ return res.getString(resId);
}
@Nullable
@@ -576,17 +586,19 @@
}
- protected class ResolveInfoPresentationGetter extends TargetPresentationGetter {
-
+ /**
+ * Loads the icon and label for the provided ResolveInfo.
+ */
+ @VisibleForTesting
+ public static class ResolveInfoPresentationGetter extends TargetPresentationGetter {
private final ResolveInfo mRi;
-
- ResolveInfoPresentationGetter(ResolveInfo ri) {
- super(ri.activityInfo.applicationInfo);
+ public ResolveInfoPresentationGetter(Context ctx, int iconDpi, ResolveInfo ri) {
+ super(ctx, iconDpi, ri.activityInfo.applicationInfo);
mRi = ri;
}
@Override
- Drawable getIconSubstitute() {
+ Drawable getIconSubstituteInternal() {
Drawable dr = null;
try {
// Do not use ResolveInfo#getIconResource() as it defaults to the app
@@ -603,20 +615,31 @@
}
@Override
- String getAppSubLabel() {
+ String getAppSubLabelInternal() {
+ // Will default to app name if no intent filter or activity label set, make sure to
+ // check if subLabel matches label before final display
return (String) mRi.loadLabel(mPm);
}
}
- protected class ActivityInfoPresentationGetter extends TargetPresentationGetter {
+ ResolveInfoPresentationGetter makePresentationGetter(ResolveInfo ri) {
+ return new ResolveInfoPresentationGetter(this, mIconDpi, ri);
+ }
+
+ /**
+ * Loads the icon and label for the provided ActivityInfo.
+ */
+ @VisibleForTesting
+ public static class ActivityInfoPresentationGetter extends TargetPresentationGetter {
private final ActivityInfo mActivityInfo;
- protected ActivityInfoPresentationGetter(ActivityInfo activityInfo) {
- super(activityInfo.applicationInfo);
+ public ActivityInfoPresentationGetter(Context ctx, int iconDpi,
+ ActivityInfo activityInfo) {
+ super(ctx, iconDpi, activityInfo.applicationInfo);
mActivityInfo = activityInfo;
}
@Override
- Drawable getIconSubstitute() {
+ Drawable getIconSubstituteInternal() {
Drawable dr = null;
try {
// Do not use ActivityInfo#getIconResource() as it defaults to the app
@@ -634,13 +657,19 @@
}
@Override
- String getAppSubLabel() {
+ String getAppSubLabelInternal() {
+ // Will default to app name if no activity label set, make sure to check if subLabel
+ // matches label before final display
return (String) mActivityInfo.loadLabel(mPm);
}
}
+ protected ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo ai) {
+ return new ActivityInfoPresentationGetter(this, mIconDpi, ai);
+ }
+
Drawable loadIconForResolveInfo(ResolveInfo ri) {
- return (new ResolveInfoPresentationGetter(ri)).getIcon();
+ return makePresentationGetter(ri).getIcon();
}
@Override
@@ -1713,34 +1742,13 @@
}
}
- // Check for applications with same name and use application name or
- // package name if necessary
- ResolvedComponentInfo rci0 = sortedComponents.get(0);
- ResolveInfo r0 = rci0.getResolveInfoAt(0);
- int start = 0;
- CharSequence r0Label = r0.loadLabel(mPm);
- mHasExtendedInfo = false;
- for (int i = 1; i < N; i++) {
- if (r0Label == null) {
- r0Label = r0.activityInfo.packageName;
+ for (ResolvedComponentInfo rci : sortedComponents) {
+ final ResolveInfo ri = rci.getResolveInfoAt(0);
+ if (ri != null) {
+ ResolveInfoPresentationGetter pg = makePresentationGetter(ri);
+ addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel());
}
- ResolvedComponentInfo rci = sortedComponents.get(i);
- ResolveInfo ri = rci.getResolveInfoAt(0);
- CharSequence riLabel = ri.loadLabel(mPm);
- if (riLabel == null) {
- riLabel = ri.activityInfo.packageName;
- }
- if (riLabel.equals(r0Label)) {
- continue;
- }
- processGroup(sortedComponents, start, (i - 1), rci0, r0Label);
- rci0 = rci;
- r0 = ri;
- r0Label = riLabel;
- start = i;
}
- // Process last group
- processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
}
postListReadyRunnable();
@@ -1782,55 +1790,6 @@
return mFilterLastUsed;
}
- private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
- ResolvedComponentInfo ro, CharSequence roLabel) {
- // Process labels from start to i
- int num = end - start+1;
- if (num == 1) {
- // No duplicate labels. Use label for entry at start
- addResolveInfoWithAlternates(ro, null, roLabel);
- } else {
- mHasExtendedInfo = true;
- boolean usePkg = false;
- final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
- final CharSequence startApp = ai.loadLabel(mPm);
- if (startApp == null) {
- usePkg = true;
- }
- if (!usePkg) {
- // Use HashSet to track duplicates
- HashSet<CharSequence> duplicates =
- new HashSet<CharSequence>();
- duplicates.add(startApp);
- for (int j = start+1; j <= end ; j++) {
- ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
- CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
- if ( (jApp == null) || (duplicates.contains(jApp))) {
- usePkg = true;
- break;
- } else {
- duplicates.add(jApp);
- }
- }
- // Clear HashSet for later use
- duplicates.clear();
- }
- for (int k = start; k <= end; k++) {
- final ResolvedComponentInfo rci = rList.get(k);
- final ResolveInfo add = rci.getResolveInfoAt(0);
- final CharSequence extraInfo;
- if (usePkg) {
- // Use package name for all entries from start to end-1
- extraInfo = add.activityInfo.packageName;
- } else {
- // Use application name for all entries from start to end-1
- extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
- }
- addResolveInfoWithAlternates(rci, extraInfo, roLabel);
- }
- }
- }
-
private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
CharSequence extraInfo, CharSequence roLabel) {
final int count = rci.getCount();
@@ -1979,31 +1938,32 @@
com.android.internal.R.layout.resolve_list_item, parent, false);
}
- public boolean showsExtendedInfo(TargetInfo info) {
- return !TextUtils.isEmpty(info.getExtendedInfo());
- }
-
public final void bindView(int position, View view) {
onBindView(view, getItem(position));
}
- private void onBindView(View view, TargetInfo info) {
+ protected void onBindView(View view, TargetInfo info) {
final ViewHolder holder = (ViewHolder) view.getTag();
if (info == null) {
holder.icon.setImageDrawable(
getDrawable(R.drawable.resolver_icon_placeholder));
return;
}
+
final CharSequence label = info.getDisplayLabel();
if (!TextUtils.equals(holder.text.getText(), label)) {
holder.text.setText(info.getDisplayLabel());
}
- if (showsExtendedInfo(info)) {
- holder.text2.setVisibility(View.VISIBLE);
- holder.text2.setText(info.getExtendedInfo());
- } else {
- holder.text2.setVisibility(View.GONE);
+
+ // Always show a subLabel for visual consistency across list items. Show an empty
+ // subLabel if the subLabel is the same as the label
+ CharSequence subLabel = info.getExtendedInfo();
+ if (TextUtils.equals(label, subLabel)) subLabel = null;
+
+ if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+ holder.text2.setText(subLabel);
}
+
if (info instanceof DisplayResolveInfo
&& !((DisplayResolveInfo) info).hasDisplayIcon()) {
new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 3303374..e8691fa 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -527,6 +527,13 @@
return (array != null) ? array.clone() : null;
}
+ /**
+ * Clones an array or returns null if the array is null.
+ */
+ public static @Nullable <T> T[] cloneOrNull(@Nullable T[] array) {
+ return (array != null) ? array.clone() : null;
+ }
+
public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
return (array != null) ? new ArraySet<T>(array) : null;
}
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index f40b461..bd4862d 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -106,8 +106,7 @@
static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- (void)apk_assets;
- return JNI_TRUE;
+ return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
}
static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index bf33ac6..d029527 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2328,4 +2328,7 @@
// OPEN: Settings > System > Aware > Info dialog
DIALOG_AWARE_STATUS = 1701;
+
+ // Open: Settings > app > bubble settings > confirmation dialog
+ DIALOG_APP_BUBBLE_SETTINGS = 1702;
}
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 4a3dfba..7065149 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -22,46 +22,43 @@
android:layout_height="wrap_content"
android:minHeight="100dp"
android:gravity="center"
- android:paddingTop="8dp"
+ android:paddingTop="24dp"
android:paddingBottom="8dp"
+ android:paddingLeft="2dp"
+ android:paddingRight="2dp"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless">
<ImageView android:id="@+id/icon"
android:layout_width="@dimen/resolver_icon_size"
android:layout_height="@dimen/resolver_icon_size"
- android:layout_marginLeft="3dp"
- android:layout_marginRight="3dp"
- android:layout_marginBottom="3dp"
android:scaleType="fitCenter" />
- <!-- Activity name -->
+ <!-- Size manually tuned to match specs -->
+ <Space android:layout_width="1dp"
+ android:layout_height="7dp"/>
+
+ <!-- App name or Direct Share target name, DS set to 2 lines -->
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:layout_marginLeft="4dp"
- android:layout_marginRight="4dp"
android:textAppearance="?attr/textAppearanceSmall"
android:textColor="?attr/textColorPrimary"
- android:textSize="12sp"
+ android:textSize="14sp"
android:fontFamily="sans-serif-condensed"
android:gravity="top|center_horizontal"
- android:minLines="2"
- android:maxLines="2"
- android:ellipsize="marquee" />
- <!-- Extended activity info to distinguish between duplicate activity names -->
+ android:lines="1"
+ android:ellipsize="end" />
+
+ <!-- Activity name if set, gone for Direct Share targets -->
<TextView android:id="@android:id/text2"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="12sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:layout_marginRight="4dp"
- android:minLines="2"
- android:maxLines="2"
+ android:lines="1"
android:gravity="top|center_horizontal"
- android:ellipsize="marquee"
- android:visibility="gone" />
+ android:ellipsize="end"/>
+
</LinearLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 362d01c..8f3f25a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1687,6 +1687,17 @@
- {@code false} for apps with targetSdkVersion < 29.
-->
<attr name="allowAudioPlaybackCapture" format="boolean" />
+ <!-- If {@code true} this app allows shared/external storage media to be
+ a sandboxed view that only contains files owned by the app.
+ <p>
+ Sandboxed apps can continue to discover and read media belonging to other
+ apps via {@code MediaStore}.
+ <p>
+ The default value is:
+ - {@code true} for apps with targetSdkVersion >= 29 (Q).
+ - {@code false} for apps with targetSdkVersion < 29.
+ -->
+ <attr name="allowExternalStorageSandbox" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
used to control access from other packages to specific components or
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 77ce8c3..2139453 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3983,6 +3983,9 @@
<!-- Whether or not to enable automatic heap dumps for the system server on debuggable builds. -->
<bool name="config_debugEnableAutomaticSystemServerHeapDumps">false</bool>
+ <!-- Trigger a heap dump if the system server pss usage exceeds this threshold. 400 MB -->
+ <integer name="config_debugSystemServerPssThresholdBytes">419430400</integer>
+
<!-- See DropBoxManagerService.
The minimum period in milliseconds between broadcasts for entries with low priority
dropbox tags. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3505994..3bbc03f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2942,6 +2942,7 @@
<public name="allowClearUserDataOnFailedRestore"/>
<public name="allowAudioPlaybackCapture"/>
<public name="secureElementName" />
+ <public name="allowExternalStorageSandbox"/>
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 45494a0..4320bf4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1582,25 +1582,25 @@
</string-array>
<!-- Error message shown when the face hardware can't be accessed. [CHAR LIMIT=50] -->
- <string name="face_error_hw_not_available">Face hardware not available.</string>
+ <string name="face_error_hw_not_available">Can\u2019t verify face. Hardware not available.</string>
<!-- Error message shown when the face hardware timer has expired and the user needs to restart the operation. [CHAR LIMIT=50] -->
<string name="face_error_timeout">Face timeout reached. Try again.</string>
- <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=50] -->
- <string name="face_error_no_space">Face can\u2019t be stored.</string>
+ <!-- Error message shown when the face hardware has run out of room for storing faces. [CHAR LIMIT=60] -->
+ <string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
<!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
- <string name="face_error_canceled">Face operation canceled.</string>
+ <string name="face_error_canceled">Face operation canceled</string>
<!-- Generic error message shown when the face authentication operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=50] -->
- <string name="face_error_user_canceled">Face authentication canceled by user.</string>
+ <string name="face_error_user_canceled">Face authentication canceled by user</string>
<!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
<string name="face_error_lockout">Too many attempts. Try again later.</string>
- <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=50] -->
- <string name="face_error_lockout_permanent">Too many attempts. Facial authentication disabled.</string>
+ <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=60] -->
+ <string name="face_error_lockout_permanent">Too many attempts. Face authentication disabled.</string>
<!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
<string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
<!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=50] -->
- <string name="face_error_not_enrolled">You haven\u2019t set up face authentication.</string>
+ <string name="face_error_not_enrolled">You haven\u2019t set up face authentication</string>
<!-- Generic error message shown when the app requests face authentication on a device without a sensor. [CHAR LIMIT=60] -->
- <string name="face_error_hw_not_present">Face authentication is not supported on this device.</string>
+ <string name="face_error_hw_not_present">Face authentication is not supported on this device</string>
<!-- Template to be used to name enrolled faces by default. [CHAR LIMIT=10] -->
<string name="face_name_template">Face <xliff:g id="faceId" example="1">%d</xliff:g></string>
@@ -4325,10 +4325,10 @@
<!-- Activity starter -->
<!-- Toast message for blocking background activity starts feature running in permissive mode -->
- <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See go/q-bg-block.</string>
+ <string name="activity_starter_block_bg_activity_starts_permissive">This background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> will be blocked in future Q builds. See g.co/dev/bgblock.</string>
<!-- Toast message for blocking background activity starts feature running in enforcing mode -->
- <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See go/q-bg-block. </string>
+ <string name="activity_starter_block_bg_activity_starts_enforcing">Background activity start from <xliff:g id="packageName" example="com.example">%1$s</xliff:g> blocked. See g.co/dev/bgblock. </string>
<!-- Keyguard strings -->
<!-- Message shown in pattern unlock after some number of unsuccessful attempts -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 35fd88e..4188bd4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3230,6 +3230,7 @@
<java-symbol type="bool" name="config_batterymeterDualTone" />
<java-symbol type="bool" name="config_debugEnableAutomaticSystemServerHeapDumps" />
+ <java-symbol type="integer" name="config_debugSystemServerPssThresholdBytes" />
<!-- Accessibility Shortcut -->
<java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 7430c7a..453bddd 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -23,6 +23,7 @@
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
import static org.hamcrest.CoreMatchers.is;
@@ -32,6 +33,7 @@
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.text.TextUtils;
import android.view.View;
import android.widget.RelativeLayout;
@@ -40,7 +42,10 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ActivityInfoPresentationGetter;
+import com.android.internal.app.ResolverActivity.ResolveInfoPresentationGetter;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.app.ResolverDataProvider.PackageManagerMockedInfo;
import com.android.internal.widget.ResolverDrawerLayout;
import org.junit.Before;
@@ -319,6 +324,50 @@
assertThat(chosen[0], is(toChoose));
}
+ @Test
+ public void getActivityLabelAndSubLabel() throws Exception {
+ ActivityInfoPresentationGetter pg;
+ PackageManagerMockedInfo info;
+
+ info = createPackageManagerMockedInfo(false);
+ pg = new ActivityInfoPresentationGetter(
+ info.ctx, 0, info.activityInfo);
+ assertThat("Label should match app label", pg.getLabel().equals(
+ info.setAppLabel));
+ assertThat("Sublabel should match activity label if set",
+ pg.getSubLabel().equals(info.setActivityLabel));
+
+ info = createPackageManagerMockedInfo(true);
+ pg = new ActivityInfoPresentationGetter(
+ info.ctx, 0, info.activityInfo);
+ assertThat("With override permission label should match activity label if set",
+ pg.getLabel().equals(info.setActivityLabel));
+ assertThat("With override permission sublabel should be empty",
+ TextUtils.isEmpty(pg.getSubLabel()));
+ }
+
+ @Test
+ public void getResolveInfoLabelAndSubLabel() throws Exception {
+ ResolveInfoPresentationGetter pg;
+ PackageManagerMockedInfo info;
+
+ info = createPackageManagerMockedInfo(false);
+ pg = new ResolveInfoPresentationGetter(
+ info.ctx, 0, info.resolveInfo);
+ assertThat("Label should match app label", pg.getLabel().equals(
+ info.setAppLabel));
+ assertThat("Sublabel should match resolve info label if set",
+ pg.getSubLabel().equals(info.setResolveInfoLabel));
+
+ info = createPackageManagerMockedInfo(true);
+ pg = new ResolveInfoPresentationGetter(
+ info.ctx, 0, info.resolveInfo);
+ assertThat("With override permission label should match resolve info label if set",
+ pg.getLabel().equals(info.setResolveInfoLabel));
+ assertThat("With override permission sublabel should be empty",
+ TextUtils.isEmpty(pg.getSubLabel()));
+ }
+
private Intent createSendImageIntent() {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
index 850b466..59634f6 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverDataProvider.java
@@ -17,11 +17,17 @@
package com.android.internal.app;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.os.UserHandle;
+import android.test.mock.MockContext;
+import android.test.mock.MockPackageManager;
+import android.test.mock.MockResources;
/**
* Utility class used by resolver tests to create mock data
@@ -71,6 +77,86 @@
return ai;
}
+ static class PackageManagerMockedInfo {
+ public Context ctx;
+ public ApplicationInfo appInfo;
+ public ActivityInfo activityInfo;
+ public ResolveInfo resolveInfo;
+ public String setAppLabel;
+ public String setActivityLabel;
+ public String setResolveInfoLabel;
+ }
+
+ static PackageManagerMockedInfo createPackageManagerMockedInfo(boolean hasOverridePermission) {
+ final String appLabel = "app_label";
+ final String activityLabel = "activity_label";
+ final String resolveInfoLabel = "resolve_info_label";
+
+ MockContext ctx = new MockContext() {
+ @Override
+ public PackageManager getPackageManager() {
+ return new MockPackageManager() {
+ @Override
+ public int checkPermission(String permName, String pkgName) {
+ if (hasOverridePermission) return PERMISSION_GRANTED;
+ return PERMISSION_DENIED;
+ }
+ };
+ }
+
+ @Override
+ public Resources getResources() {
+ return new MockResources() {
+ @Override
+ public String getString(int id) throws NotFoundException {
+ if (id == 1) return appLabel;
+ if (id == 2) return activityLabel;
+ if (id == 3) return resolveInfoLabel;
+ return null;
+ }
+ };
+ }
+ };
+
+ ApplicationInfo appInfo = new ApplicationInfo() {
+ @Override
+ public CharSequence loadLabel(PackageManager pm) {
+ return appLabel;
+ }
+ };
+ appInfo.labelRes = 1;
+
+ ActivityInfo activityInfo = new ActivityInfo() {
+ @Override
+ public CharSequence loadLabel(PackageManager pm) {
+ return activityLabel;
+ }
+ };
+ activityInfo.labelRes = 2;
+ activityInfo.applicationInfo = appInfo;
+
+ ResolveInfo resolveInfo = new ResolveInfo() {
+ @Override
+ public CharSequence loadLabel(PackageManager pm) {
+ return resolveInfoLabel;
+ }
+ };
+ resolveInfo.activityInfo = activityInfo;
+ resolveInfo.resolvePackageName = "super.fake.packagename";
+ resolveInfo.labelRes = 3;
+
+ PackageManagerMockedInfo mockedInfo = new PackageManagerMockedInfo();
+ mockedInfo.activityInfo = activityInfo;
+ mockedInfo.appInfo = appInfo;
+ mockedInfo.ctx = ctx;
+ mockedInfo.resolveInfo = resolveInfo;
+ mockedInfo.setAppLabel = appLabel;
+ mockedInfo.setActivityLabel = activityLabel;
+ mockedInfo.setResolveInfoLabel = resolveInfoLabel;
+
+ return mockedInfo;
+ }
+
static Intent createResolverIntent(int i) {
return new Intent("intentAction" + i);
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 3c8794f..ed198e6 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,7 @@
<permission name="android.permission.ACTIVITY_EMBEDDING"/>
<permission name="android.permission.FORCE_STOP_PACKAGES"/>
<permission name="android.permission.GET_APP_OPS_STATS"/>
+ <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 66a5477..7b7599f 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -29,6 +29,7 @@
#include "androidfw/Asset.h"
#include "androidfw/Idmap.h"
+#include "androidfw/misc.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/Util.h"
@@ -39,8 +40,10 @@
static const std::string kResourcesArsc("resources.arsc");
-ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path)
- : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path) {
+ApkAssets::ApkAssets(ZipArchiveHandle unmanaged_handle,
+ const std::string& path,
+ time_t last_mod_time)
+ : zip_handle_(unmanaged_handle, ::CloseArchive), path_(path), last_mod_time_(last_mod_time) {
}
std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
@@ -116,8 +119,10 @@
return {};
}
+ time_t last_mod_time = getFileModDate(path.c_str());
+
// Wrap the handle in a unique_ptr so it gets automatically closed.
- std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path));
+ std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(unmanaged_handle, path, last_mod_time));
// Find the resource table.
::ZipString entry_name(kResourcesArsc.c_str());
@@ -248,4 +253,8 @@
return result == -1;
}
+bool ApkAssets::IsUpToDate() const {
+ return last_mod_time_ == getFileModDate(path_.c_str());
+}
+
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 35bbb58..49fc82b 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -84,6 +84,8 @@
return idmap_asset_.get() != nullptr;
}
+ bool IsUpToDate() const;
+
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
@@ -95,12 +97,13 @@
// Creates an Asset from any file on the file system.
static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
- ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path);
+ ApkAssets(ZipArchiveHandle unmanaged_handle, const std::string& path, time_t last_mod_time);
using ZipArchivePtr = std::unique_ptr<ZipArchive, void(*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
const std::string path_;
+ time_t last_mod_time_;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index fb60a96..89a9b99 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -84,6 +84,7 @@
}
CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ ATRACE_CALL();
if (!mRenderThread.getGrContext()) {
return CopyResult::UnknownError;
}
@@ -104,6 +105,7 @@
CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
const Rect& srcRect, SkBitmap* bitmap) {
+ ATRACE_CALL();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
} else {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1bcb819..16240b4 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -162,6 +162,7 @@
}
bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
+ ATRACE_NAME("TextureView#getBitmap");
auto& thread = RenderThread::getInstance();
return thread.queue().runSync([&]() -> bool {
return thread.readback().copyLayerInto(layer, &bitmap) == CopyResult::Success;
@@ -347,6 +348,7 @@
}
int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+ ATRACE_NAME("HardwareBitmap readback");
RenderThread& thread = RenderThread::getInstance();
if (gettid() == thread.getTid()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 1708d3c..3fed6b0 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -222,7 +222,17 @@
const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height);
ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
- windowInfo.bufferCount = std::max<uint32_t>(VulkanSurface::sMaxBufferCount, caps.minImageCount);
+ int query_value;
+ int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
+ if (err != 0 || query_value < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
+ query_value);
+ return nullptr;
+ }
+ auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
+ windowInfo.bufferCount = min_undequeued_buffers
+ + std::max(VulkanSurface::sTargetBufferCount, caps.minImageCount);
if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
// Application must settle for fewer images than desired:
windowInfo.bufferCount = caps.maxImageCount;
@@ -357,10 +367,9 @@
return false;
}
- // Lower layer insists that we have at least two buffers.
- err = native_window_set_buffer_count(window, std::max(2, windowInfo.bufferCount));
+ err = native_window_set_buffer_count(window, windowInfo.bufferCount);
if (err != 0) {
- ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%d) failed: %s (%d)",
+ ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
windowInfo.bufferCount, strerror(-err), err);
return false;
}
@@ -392,7 +401,7 @@
}
void VulkanSurface::releaseBuffers() {
- for (uint32_t i = 0; i < VulkanSurface::sMaxBufferCount; i++) {
+ for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
index 418d40f..305483f 100644
--- a/libs/hwui/renderthread/VulkanSurface.h
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -83,14 +83,19 @@
* as private to this class.
*
*/
- static constexpr int sMaxBufferCount = 3;
+
+ // How many buffers we want to be able to use ourselves. We want 1 in active rendering with
+ // 1 more queued, so 2. This will be added to NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, which is
+ // how many buffers the consumer needs (eg, 1 for SurfaceFlinger), getting to a typically
+ // triple-buffered queue as a result.
+ static constexpr uint32_t sTargetBufferCount = 2;
struct WindowInfo {
SkISize size;
PixelFormat pixelFormat;
android_dataspace dataspace;
int transform;
- int bufferCount;
+ size_t bufferCount;
uint64_t windowUsageFlags;
// size of the ANativeWindow if the inverse of transform requires us to swap width/height
@@ -111,7 +116,8 @@
const SkISize& maxSize);
void releaseBuffers();
- NativeBufferInfo mNativeBuffers[VulkanSurface::sMaxBufferCount];
+ // TODO: Just use a vector?
+ NativeBufferInfo mNativeBuffers[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
sp<ANativeWindow> mNativeWindow;
WindowInfo mWindowInfo;
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 44e0a65..a528636 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -18,7 +18,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.captiveportallogin"
- android:versionCode="11"
+ android:versionCode="200000000"
android:versionName="Q-initial">
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
diff --git a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index 395eac1..2fe9d21 100644
--- a/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -18,8 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- android:visibility="gone">
+ android:fitsSystemWindows="true">
<LinearLayout
android:id="@+id/container"
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 383aba4..5c915b8 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -19,7 +19,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/notification_view"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="@color/notification_shade_background_color">
<View
android:id="@+id/glass_pane"
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 0594dce..9a074dd 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -64,7 +64,6 @@
<include layout="@layout/car_top_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
/>
</LinearLayout>
@@ -80,6 +79,13 @@
android:layout_height="match_parent"
android:visibility="invisible"/>
+ <include layout="@layout/notification_center_activity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginBottom="112dp"
+ android:visibility="invisible"
+ />
+
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index c9f9dea..d946fbc 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -21,6 +21,7 @@
<string name="config_systemUIFactoryComponent" translatable="false">
com.android.systemui.CarSystemUIFactory
</string>
+
<bool name="config_enableFullscreenUserSwitcher">true</bool>
<!-- configure which system ui bars should be displayed -->
@@ -28,30 +29,12 @@
<bool name="config_enableRightNavigationBar">false</bool>
<bool name="config_enableBottomNavigationBar">true</bool>
- <!-- SystemUI Services: The classes of the stuff to start. This is duplicated from core
- SystemUi b/c it can't be overlayed at this level for now
- -->
- <string-array name="config_systemUIServiceComponents" translatable="false">
- <item>com.android.systemui.Dependency$DependencyCreator</item>
- <item>com.android.systemui.util.NotificationChannels</item>
- <item>com.android.systemui.notifications.NotificationsUI</item>
- <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
- <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
- <item>com.android.systemui.recents.Recents</item>
- <item>com.android.systemui.volume.VolumeUI</item>
- <item>com.android.systemui.stackdivider.Divider</item>
- <item>com.android.systemui.SystemBars</item>
- <item>com.android.systemui.usb.StorageNotification</item>
- <item>com.android.systemui.power.PowerUI</item>
- <item>com.android.systemui.media.RingtonePlayer</item>
- <item>com.android.systemui.keyboard.KeyboardUI</item>
- <item>com.android.systemui.pip.PipUI</item>
- <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
- <item>@string/config_systemUIVendorServiceComponent</item>
- <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
- <item>com.android.systemui.LatencyTester</item>
- <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
- <item>com.android.systemui.ScreenDecorations</item>
- <item>com.android.systemui.SliceBroadcastRelayHandler</item>
- </string-array>
+ <bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
+ <bool name="config_enablePersistentDockedActivity">false</bool>
+ <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
+
+ <!-- How many icons may be shown at once in the system bar. Includes any
+ slots that may be reused for things like IME control. -->
+ <integer name="config_maxNotificationIcons">0</integer>
+
</resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
deleted file mode 100644
index 2e2f3b7..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.notifications;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ServiceConnection;
-import android.content.res.Configuration;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.car.notification.CarNotificationListener;
-import com.android.car.notification.CarNotificationView;
-import com.android.car.notification.CarUxRestrictionManagerWrapper;
-import com.android.car.notification.NotificationClickHandlerFactory;
-import com.android.car.notification.NotificationViewController;
-import com.android.car.notification.PreprocessingManager;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.SystemUI;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.car.CarStatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
-/**
- * Standalone SystemUI for displaying Notifications that have been designed to be used in the car
- */
-public class NotificationsUI extends SystemUI
- implements ConfigurationController.ConfigurationListener {
-
- private static final String TAG = "NotificationsUI";
- // used to calculate how fast to open or close the window
- private static final float DEFAULT_FLING_VELOCITY = 0;
- // max time a fling animation takes
- private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
- // acceleration rate for the fling animation
- private static final float FLING_SPEED_UP_FACTOR = 0.6f;
- private CarNotificationListener mCarNotificationListener;
- private CarUxRestrictionsManager mCarUxRestrictionsManager;
- private NotificationClickHandlerFactory mClickHandlerFactory;
- private Car mCar;
- private ViewGroup mCarNotificationWindow;
- private NotificationViewController mNotificationViewController;
- private boolean mIsShowing;
- private boolean mIsTracking;
- private boolean mNotificationListAtBottom;
- private boolean mNotificationListAtBottomAtTimeOfTouch;
- private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper =
- new CarUxRestrictionManagerWrapper();
- // Used in the Notification panel touch listener
- private GestureDetector mGestureDetector;
- // Used in scrollable content of the notifications
- private GestureDetector mScrollUpDetector;
- private View mContent;
- private View.OnTouchListener mOnTouchListener;
- private FlingAnimationUtils mFlingAnimationUtils;
- private static int sSettleOpenPercentage;
- private static int sSettleClosePercentage;
- private CarStatusBar mCarStatusBar;
-
- /**
- * Inits the window that hosts the notifications and establishes the connections
- * to the car related services.
- */
- @Override
- public void start() {
- sSettleOpenPercentage = mContext.getResources().getInteger(
- R.integer.notification_settle_open_percentage);
- sSettleClosePercentage = mContext.getResources().getInteger(
- R.integer.notification_settle_close_percentage);
- WindowManager windowManager =
- (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mFlingAnimationUtils = new FlingAnimationUtils(mContext,
- FLING_ANIMATION_MAX_TIME, FLING_SPEED_UP_FACTOR);
- mCarNotificationListener = new CarNotificationListener();
- // create a notification click handler that closes the notification ui if the an activity
- // is launched successfully
- mClickHandlerFactory = new NotificationClickHandlerFactory(
- IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE)),
- launchResult -> {
- if (launchResult == ActivityManager.START_TASK_TO_FRONT
- || launchResult == ActivityManager.START_SUCCESS) {
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- }
- });
- mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
- mClickHandlerFactory);
- mCar = Car.createCar(mContext, mCarConnectionListener);
- mCar.connect();
- NotificationGestureListener gestureListener = new NotificationGestureListener();
- mGestureDetector = new GestureDetector(mContext, gestureListener);
- mScrollUpDetector = new GestureDetector(mContext, new ScrollUpDetector());
- mOnTouchListener = new NotificationPanelTouchListener();
- mCarNotificationWindow = (ViewGroup) View.inflate(new ContextThemeWrapper(mContext,
- R.style.Theme_Notification),
- R.layout.navigation_bar_window, null);
- mCarNotificationWindow
- .setBackgroundColor(mContext.getColor(R.color.notification_shade_background_color));
- inflateNotificationContent();
-
- WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- layoutParams.setTitle("Car Notification Window");
- // start in the hidden state
- mCarNotificationWindow.setVisibility(View.GONE);
- windowManager.addView(mCarNotificationWindow, layoutParams);
-
- // Add this object to the SystemUI component registry such that the status bar
- // can get a reference to it.
- putComponent(NotificationsUI.class, this);
- Dependency.get(ConfigurationController.class).addCallback(this);
- }
-
- @SuppressLint("ClickableViewAccessibility")
- private void inflateNotificationContent() {
- if (mNotificationViewController != null) {
- mNotificationViewController.disable();
- }
- mCarNotificationWindow.removeAllViews();
-
- mContent = View.inflate(new ContextThemeWrapper(mContext,
- com.android.car.notification.R.style.Theme_Notification),
- R.layout.notification_center_activity,
- mCarNotificationWindow);
- // set the click handler such that we can dismiss the UI when a notification is clicked
- CarNotificationView noteView = mCarNotificationWindow.findViewById(R.id.notification_view);
- noteView.setClickHandlerFactory(mClickHandlerFactory);
-
- mContent.setOnTouchListener(mOnTouchListener);
- // set initial translation after size is calculated
- mContent.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- mContent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- if (!mIsShowing && !mIsTracking) {
- mContent.setTranslationY(mContent.getHeight() * -1);
- }
- }
- });
-
- RecyclerView notificationList = mCarNotificationWindow.findViewById(R.id.notifications);
- // register a scroll listener so we can figure out if we are at the bottom of the
- // list of notifications
- notificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- if (!notificationList.canScrollVertically(1)) {
- mNotificationListAtBottom = true;
- return;
- }
- mNotificationListAtBottom = false;
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- });
- // add a touch listener such that when the user scrolls up and they are at the bottom
- // of the list we can start the closing of the view.
- notificationList.setOnTouchListener(new NotificationListTouchListener());
-
- // There's a view installed at a higher z-order such that we can intercept the ACTION_DOWN
- // to set the initial click state.
- mCarNotificationWindow.findViewById(R.id.glass_pane).setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
- // register the down event with the gesture detectors so then know where the down
- // started. This is needed because at this point we don't know which listener
- // is going to handle scroll and fling events.
- mGestureDetector.onTouchEvent(event);
- mScrollUpDetector.onTouchEvent(event);
- }
- return false;
- });
-
- mNotificationViewController = new NotificationViewController(
- mCarNotificationWindow
- .findViewById(com.android.car.notification.R.id.notification_view),
- PreprocessingManager.getInstance(mContext),
- mCarNotificationListener,
- mCarUxRestrictionManagerWrapper);
- mNotificationViewController.enable();
- }
-
- // allows for day night switch
- @Override
- public void onConfigChanged(Configuration newConfig) {
- inflateNotificationContent();
- }
-
- public void setStatusBar(CarStatusBar carStatusBar) {
- mCarStatusBar = carStatusBar;
- }
-
- public View.OnTouchListener getDragDownListener() {
- return mOnTouchListener;
- }
-
- /**
- * This listener is attached to the notification list UI to intercept gestures if the user
- * is scrolling up when the notification list is at the bottom
- */
- private class ScrollUpDetector extends GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- return distanceY > 0;
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent motionEvent) {
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return false;
- }
- }
-
- private class NotificationListTouchListener implements View.OnTouchListener {
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // reset mNotificationListAtBottomAtTimeOfTouch here since the "glass pane" will not
- // get the up event
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- boolean wasScrolledUp = mScrollUpDetector.onTouchEvent(event);
-
- if (mIsTracking
- || (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom
- && wasScrolledUp)) {
- mOnTouchListener.onTouch(v, event);
- // touch event should not be propagated further
- return true;
- }
- return false;
- }
- }
-
- /**
- * Touch listener installed on the notification panel. It is also used by the Nav and StatusBar
- */
- private class NotificationPanelTouchListener implements View.OnTouchListener {
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- boolean consumed = mGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- if (!mIsTracking || event.getActionMasked() != MotionEvent.ACTION_UP) {
- return false;
- }
-
- float percentFromBottom =
- Math.abs(mContent.getTranslationY() / mContent.getHeight()) * 100;
- if (mIsShowing) {
- if (percentFromBottom < sSettleOpenPercentage) {
- // panel started to close but did not cross minimum threshold thus we open
- // it back up
- openCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
- }
- // panel was lifted more than the threshold thus we close it the rest of the way
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
- }
-
- if (percentFromBottom > sSettleClosePercentage) {
- // panel was only peeked at thus close it back up
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
- }
- // panel has been open more than threshold thus open it the rest of the way
- openCarNotifications(DEFAULT_FLING_VELOCITY);
- return true;
-
- }
- }
-
- /**
- * Listener called by mGestureDetector. This will be initiated from the
- * NotificationPanelTouchListener
- */
- private class NotificationGestureListener extends GestureDetector.SimpleOnGestureListener {
- private static final int SWIPE_UP_MIN_DISTANCE = 75;
- private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
- private static final int SWIPE_MAX_OFF_PATH = 75;
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- if (mCarStatusBar == null || !mCarStatusBar.getIsUserSetup()) {
- return true;
- }
- boolean isDown = event1.getY() - event2.getY() < 0;
- // CarStatusBar and NavigationBar are identical so avoid the touch if it
- // starts from NavigationBar to open.
- if (event1.getRawY() > mCarNotificationWindow.getHeight() && isDown
- && mCarNotificationWindow.getVisibility() == View.GONE) {
- mIsTracking = false;
- return true;
- }
- mIsTracking = true;
- mCarNotificationWindow.setVisibility(View.VISIBLE);
-
- mContent.setTranslationY(Math.min(mContent.getTranslationY() - distanceY, 0));
- if (mContent.getTranslationY() == 0) {
- mIsTracking = false;
- }
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
- || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
- // swipe was not vertical or was not fast enough
- return false;
- }
-
- boolean isUp = velocityY < 0;
- float distanceDelta = Math.abs(event1.getY() - event2.getY());
- if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
- // fling up
- mIsTracking = false;
- closeCarNotifications(Math.abs(velocityY));
- return true;
-
- } else if (!isUp && distanceDelta > SWIPE_DOWN_MIN_DISTANCE
- && (event1.getRawY() < mCarNotificationWindow.getHeight()
- || mCarNotificationWindow.getVisibility() == View.VISIBLE)) {
- // fling down
- mIsTracking = false;
- openCarNotifications(velocityY);
- return true;
- }
-
- return false;
- }
- }
-
- /**
- * Connection callback to establish UX Restrictions
- */
- private ServiceConnection mCarConnectionListener = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
- Car.CAR_UX_RESTRICTION_SERVICE);
- mCarUxRestrictionManagerWrapper
- .setCarUxRestrictionsManager(mCarUxRestrictionsManager);
- PreprocessingManager preprocessingManager = PreprocessingManager.getInstance(
- mContext);
- preprocessingManager
- .setCarUxRestrictionManagerWrapper(mCarUxRestrictionManagerWrapper);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car not connected in CarConnectionListener", e);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.e(TAG, "Car service disconnected unexpectedly");
- }
- };
-
- /**
- * Toggles the visibility of the notifications
- */
- public void toggleShowingCarNotifications() {
- if (mCarNotificationWindow.getVisibility() == View.VISIBLE) {
- closeCarNotifications(DEFAULT_FLING_VELOCITY);
- return;
- }
- openCarNotifications(DEFAULT_FLING_VELOCITY);
- }
-
- /**
- * Hides the notifications
- */
- public void closeCarNotifications(float velocityY) {
- float closedTranslation = mContent.getHeight() * -1;
- ValueAnimator animator =
- ValueAnimator.ofFloat(mContent.getTranslationY(), closedTranslation);
- animator.addUpdateListener(
- animation -> mContent.setTranslationY((Float) animation.getAnimatedValue()));
- mFlingAnimationUtils.apply(
- animator, mContent.getTranslationY(), closedTranslation, velocityY);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCarNotificationWindow.setVisibility(View.GONE);
- }
- });
- animator.start();
- mNotificationViewController.disable();
- mIsShowing = false;
- mIsTracking = false;
- RecyclerView notificationListView = mCarNotificationWindow.findViewById(R.id.notifications);
- notificationListView.scrollToPosition(0);
- }
-
- /**
- * Sets the notifications to visible
- */
- public void openCarNotifications(float velocityY) {
- if (mCarStatusBar == null || !mCarStatusBar.getIsUserSetup()) {
- return;
- }
- mCarNotificationWindow.setVisibility(View.VISIBLE);
-
- ValueAnimator animator = ValueAnimator.ofFloat(mContent.getTranslationY(), 0);
- animator.addUpdateListener(
- animation -> mContent.setTranslationY((Float) animation.getAnimatedValue()));
- mFlingAnimationUtils.apply(animator, mContent.getTranslationY(), 0, velocityY);
- animator.start();
-
- mNotificationViewController.enable();
- mIsShowing = true;
- mIsTracking = false;
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index afefa1b..a0f2367 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -38,6 +38,7 @@
private CarStatusBar mCarStatusBar;
private Context mContext;
private View mLockScreenButtons;
+ // used to wire in open/close gestures for notifications
private OnTouchListener mStatusBarWindowTouchListener;
@@ -65,26 +66,45 @@
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
}
+ // needs to be clickable so that it will receive ACTION_MOVE events
+ setClickable(true);
}
+ // Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mStatusBarWindowTouchListener == null) {
- return false;
+ if (mStatusBarWindowTouchListener != null) {
+ // forward touch events to the status bar window so it can add a drag down
+ // windows if required (Notification shade)
+ mStatusBarWindowTouchListener.onTouch(this, ev);
}
- // forward touch events to the status bar window so it can add a drag down
- // windows if required (Notification shade)
- mStatusBarWindowTouchListener.onTouch(this, ev);
- return false;
+ return super.onInterceptTouchEvent(ev);
}
+
void setStatusBar(CarStatusBar carStatusBar) {
mCarStatusBar = carStatusBar;
- mStatusBarWindowTouchListener = carStatusBar.getStatusBarWindowTouchListener();
+ }
+
+ /**
+ * Set a touch listener that will be called from onInterceptTouchEvent and onTouchEvent
+ *
+ * @param statusBarWindowTouchListener The listener to call from touch and intercept touch
+ */
+ void setStatusBarWindowTouchListener(OnTouchListener statusBarWindowTouchListener) {
+ mStatusBarWindowTouchListener = statusBarWindowTouchListener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mStatusBarWindowTouchListener != null) {
+ mStatusBarWindowTouchListener.onTouch(this, event);
+ }
+ return super.onTouchEvent(event);
}
protected void onNotificationsClick(View v) {
- mCarStatusBar.toggleCarNotifications();
+ mCarStatusBar.togglePanel();
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index abe9be8..9bcc1ab 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -16,17 +16,29 @@
package com.android.systemui.statusbar.car;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.car.drivingstate.CarDrivingStateEvent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.view.GestureDetector;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.notification.CarNotificationListener;
+import com.android.car.notification.CarNotificationView;
+import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationClickHandlerFactory;
+import com.android.car.notification.NotificationViewController;
+import com.android.car.notification.PreprocessingManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.CarSystemUIFactory;
@@ -37,7 +49,6 @@
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.notifications.NotificationsUI;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -70,7 +81,6 @@
private BatteryMeterView mBatteryMeterView;
private Drawable mNotificationPanelBackground;
- private ConnectedDeviceSignalController mConnectedDeviceSignalController;
private ViewGroup mNavigationBarWindow;
private ViewGroup mLeftNavigationBarWindow;
private ViewGroup mRightNavigationBarWindow;
@@ -90,6 +100,17 @@
private DrivingStateHelper mDrivingStateHelper;
private SwitchToGuestTimer mSwitchToGuestTimer;
+ // The container for the notifications.
+ private CarNotificationView mNotificationView;
+ private RecyclerView mNotificationList;
+ // The state of if the notification list is currently showing the bottom.
+ private boolean mNotificationListAtBottom;
+ // Was the notification list at the bottom when the user first touched the screen
+ private boolean mNotificationListAtBottomAtTimeOfTouch;
+ // To be attached to the navigation bars such that they can close the notification panel if
+ // it's open.
+ private View.OnTouchListener mNavBarNotificationTouchListener;
+
@Override
public void start() {
// get the provisioned state before calling the parent class since it's that flow that
@@ -223,7 +244,6 @@
* Switch to the keyguard applicable content contained in the nav bars
*/
private void updateNavBarForKeyguardContent() {
- getComponent(NotificationsUI.class).closeCarNotifications(0);
if (mNavigationBarView != null) {
mNavigationBarView.showKeyguardButtons();
}
@@ -255,17 +275,150 @@
// when a device has connected by bluetooth.
mBatteryMeterView.setVisibility(View.GONE);
});
- addTemperatureViewToController(mStatusBarWindow);
+
+ connectNotificationsUI();
+ }
+
+ /**
+ * Attach the notification listeners and controllers to the UI as well as build all the
+ * touch listeners needed for opening and closing the notification panel
+ */
+ private void connectNotificationsUI() {
+ // Attached to the status bar to detect pull down of the notification shade.
+ GestureDetector openGestureDetector = new GestureDetector(mContext,
+ new OpenNotificationGestureListener() {
+ @Override
+ protected void openNotification() {
+ animateExpandNotificationsPanel();
+ }
+ });
+ // Attached to the notification ui to detect close request of the notification shade.
+ GestureDetector closeGestureDetector = new GestureDetector(mContext,
+ new CloseNotificationGestureListener() {
+ @Override
+ protected void close() {
+ animateCollapsePanels();
+ }
+ });
+ // Attached to the NavBars to close the notification shade
+ GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+ new NavBarCloseNotificationGestureListener() {
+ @Override
+ protected void close() {
+ animateCollapsePanels();
+ }
+ });
+ mNavBarNotificationTouchListener =
+ (v, event) -> navBarCloseNotificationGestureDetector.onTouchEvent(event);
+
// The following are the ui elements that the user would call the status bar.
// This will set the status bar so it they can make call backs.
CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
topBar.setStatusBar(this);
- CarNavigationBarView qsTopBar = mStatusBarWindow.findViewById(R.id.qs_car_top_bar);
- qsTopBar.setStatusBar(this);
- getComponent(NotificationsUI.class).setStatusBar(this);
+ topBar.setStatusBarWindowTouchListener((v1, event1) ->
+ openGestureDetector.onTouchEvent(event1));
+
+ NotificationClickHandlerFactory clickHandlerFactory = new NotificationClickHandlerFactory(
+ mBarService,
+ launchResult -> {
+ if (launchResult == ActivityManager.START_TASK_TO_FRONT
+ || launchResult == ActivityManager.START_SUCCESS) {
+ animateCollapsePanels();
+ }
+ });
+ CarNotificationListener carNotificationListener = new CarNotificationListener();
+ CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper =
+ new CarUxRestrictionManagerWrapper();
+ carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper,
+ clickHandlerFactory);
+
+ mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
+ View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
+ mNotificationView.setClickHandlerFactory(clickHandlerFactory);
+
+ // The glass pane is used to view touch events before passed to the notification list.
+ // This allows us to initialize gesture listeners and detect when to close the notifications
+ glassPane.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
+ // Pass the down event to gesture detector so that it knows where the touch event
+ // started.
+ closeGestureDetector.onTouchEvent(event);
+ }
+ return false;
+ });
+ mNotificationList = mNotificationView.findViewById(R.id.notifications);
+ mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ if (!mNotificationList.canScrollVertically(1)) {
+ mNotificationListAtBottom = true;
+ return;
+ }
+ mNotificationListAtBottom = false;
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ });
+ mNotificationList.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ boolean handled = false;
+ if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
+ handled = closeGestureDetector.onTouchEvent(event);
+ }
+ // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
+ // the event has been passed to the closeGestureDetector above, such that the
+ // closeGestureDetector sees the up event before the state has changed.
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ return handled;
+ }
+ });
+
+ NotificationViewController mNotificationViewController = new NotificationViewController(
+ mNotificationView,
+ PreprocessingManager.getInstance(mContext),
+ carNotificationListener,
+ carUxRestrictionManagerWrapper);
+ mNotificationViewController.enable();
}
@Override
+ public void animateExpandNotificationsPanel() {
+ if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
+ return;
+ }
+ // scroll to top
+ mNotificationList.scrollToPosition(0);
+ mStatusBarWindowController.setPanelVisible(true);
+ mNotificationView.setVisibility(View.VISIBLE);
+ // let the status bar know that the panel is open
+ setPanelExpanded(true);
+ }
+
+ @Override
+ public void animateCollapsePanels(int flags, boolean force, boolean delayed,
+ float speedUpFactor) {
+ super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
+ if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
+ return;
+ }
+ mStatusBarWindowController.setStatusBarFocusable(false);
+ mStatusBarWindow.cancelExpandHelper();
+ mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor);
+ mStatusBarWindowController.setPanelVisible(false);
+ mNotificationView.setVisibility(View.INVISIBLE);
+ // let the status bar know that the panel is cloased
+ setPanelExpanded(false);
+ }
+
+
+ @Override
protected QS createDefaultQSFragment() {
return new CarQSFragment();
}
@@ -338,8 +491,8 @@
lp.setTitle("CarNavigationBar");
lp.windowAnimations = 0;
mWindowManager.addView(mNavigationBarWindow, lp);
- mNavigationBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
}
+
if (mShowLeft) {
int width = mContext.getResources().getDimensionPixelSize(
R.dimen.car_left_navigation_bar_width);
@@ -389,6 +542,7 @@
}
mNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mNavigationBarView);
+ mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
private void buildLeft(int layout) {
@@ -400,6 +554,7 @@
}
mLeftNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mLeftNavigationBarView);
+ mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
@@ -412,6 +567,7 @@
}
mRightNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mRightNavigationBarView);
+ mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
}
@Override
@@ -435,8 +591,6 @@
pw.println(mCarBatteryController);
pw.print(" mBatteryMeterView=");
pw.println(mBatteryMeterView);
- pw.print(" mConnectedDeviceSignalController=");
- pw.println(mConnectedDeviceSignalController);
pw.print(" mNavigationBarView=");
pw.println(mNavigationBarView);
@@ -456,11 +610,6 @@
}
}
- @Override
- protected View.OnTouchListener getStatusBarWindowTouchListener() {
- // Gets the car specific notification touch listener
- return getComponent(NotificationsUI.class).getDragDownListener();
- }
@Override
public void showBatteryView() {
@@ -585,15 +734,6 @@
true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
}
- @Override
- public void animateExpandNotificationsPanel() {
- // Because space is usually constrained in the auto use-case, there should not be a
- // pinned notification when the shade has been expanded. Ensure this by removing all heads-
- // up notifications.
- mHeadsUpManager.releaseAllImmediately();
- super.animateExpandNotificationsPanel();
- }
-
/**
* Ensures that relevant child views are appropriately recreated when the device's density
* changes.
@@ -620,12 +760,73 @@
return mUserSetup;
}
- public void toggleCarNotifications() {
- getComponent(NotificationsUI.class).toggleShowingCarNotifications();
+
+ // TODO: add settle down/up logic
+ private static final int SWIPE_UP_MIN_DISTANCE = 75;
+ private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
+ private static final int SWIPE_MAX_OFF_PATH = 75;
+ private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+ // Only responsible for open hooks. Since once the panel opens it covers all elements
+ // there is no need to merge with close.
+ private abstract class OpenNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isDown = velocityY > 0;
+ float distanceDelta = Math.abs(event1.getY() - event2.getY());
+ if (isDown && distanceDelta > SWIPE_DOWN_MIN_DISTANCE) {
+ openNotification();
+ return true;
+ }
+
+ return false;
+ }
+ protected abstract void openNotification();
}
- @Override
- public void maybeEscalateHeadsUp() {
- // Never send full screen intent in car.
+ // to be installed on the open panel notification panel
+ private abstract class CloseNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isUp = velocityY < 0;
+ float distanceDelta = Math.abs(event1.getY() - event2.getY());
+ if (isUp && distanceDelta > SWIPE_UP_MIN_DISTANCE) {
+ close();
+ return true;
+ }
+ return false;
+ }
+ protected abstract void close();
+ }
+
+ // to be installed on the nav bars
+ private abstract class NavBarCloseNotificationGestureListener extends
+ CloseNotificationGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ close();
+ return super.onSingleTapUp(e);
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ close();
+ super.onLongPress(e);
+ }
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index f896cf1..0a167d9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -38,7 +38,9 @@
public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- mParent.setVisibility(View.VISIBLE);
+ // Hide the user grid by default. It will only be made visible by clicking on a cancel
+ // button in a bouncer.
+ hide();
View container = mParent.findViewById(R.id.container);
// Initialize user grid.
@@ -49,10 +51,6 @@
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(this::onUserSelected);
- // Hide the user grid by default. It will only be made visible by clicking on a cancel
- // button in a bouncer.
- hide();
-
mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
}
@@ -61,21 +59,21 @@
* Makes user grid visible.
*/
public void show() {
- mUserGridView.setVisibility(View.VISIBLE);
+ mParent.setVisibility(View.VISIBLE);
}
/**
* Hides the user grid.
*/
public void hide() {
- mUserGridView.setVisibility(View.INVISIBLE);
+ mParent.setVisibility(View.INVISIBLE);
}
/**
* @return {@code true} if user grid is visible, {@code false} otherwise.
*/
public boolean isVisible() {
- return mUserGridView.getVisibility() == View.VISIBLE;
+ return mParent.getVisibility() == View.VISIBLE;
}
/**
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index d409758..45a59a3 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="android.ext.services"
- android:versionCode="1"
+ android:versionCode="200000000"
android:versionName="1"
coreApp="true">
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 9b60dc3..8d8e222 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,8 +17,12 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.networkstack"
- android:sharedUserId="android.uid.networkstack">
+ package="com.android.networkstack"
+ android:sharedUserId="android.uid.networkstack"
+ android:versionCode="200000000"
+ android:versionName="29 system image"
+>
+
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
new file mode 100644
index 0000000..7f4442d
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+ name: "SettingsLibAdaptiveIcon",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "SettingsLibTile"
+ ],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
new file mode 100644
index 0000000..256b8f3
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
new file mode 100644
index 0000000..76d106a
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<resources>
+ <color name="homepage_generic_icon_background">#1A73E8</color>
+
+ <color name="bt_outline_color">#1f000000</color> <!-- icon outline color -->
+</resources>
diff --git a/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
new file mode 100644
index 0000000..7f5b58c
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+ -->
+
+<resources>
+ <!-- Dashboard foreground image inset (from background edge to foreground edge) -->
+ <dimen name="dashboard_tile_foreground_image_inset">6dp</dimen>
+
+ <!-- Stroke size of adaptive outline -->
+ <dimen name="adaptive_outline_stroke">1dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
new file mode 100644
index 0000000..fc93650
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIcon.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import static androidx.annotation.VisibleForTesting.NONE;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.drawer.Tile;
+
+/**
+ * Adaptive icon that can set background color
+ */
+public class AdaptiveIcon extends LayerDrawable {
+
+ private static final String TAG = "AdaptiveHomepageIcon";
+
+ @VisibleForTesting(otherwise = NONE)
+ int mBackgroundColor = -1;
+ private AdaptiveConstantState mAdaptiveConstantState;
+
+ public AdaptiveIcon(Context context, Drawable foreground) {
+ super(new Drawable[]{
+ new AdaptiveIconShapeDrawable(context.getResources()),
+ foreground
+ });
+ final int insetPx = context.getResources()
+ .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+ setLayerInset(1 /* index */, insetPx, insetPx, insetPx, insetPx);
+ mAdaptiveConstantState = new AdaptiveConstantState(context, foreground);
+ }
+
+ /**
+ * According {@code tile} metaData to set background color
+ */
+ public void setBackgroundColor(Context context, Tile tile) {
+ final Bundle metaData = tile.getMetaData();
+ try {
+ if (metaData != null) {
+ // Load from bg.argb first
+ int bgColor = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB,
+ 0 /* default */);
+ // Not found, load from bg.hint
+ if (bgColor == 0) {
+ final int colorRes = metaData.getInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ 0 /* default */);
+ if (colorRes != 0) {
+ bgColor = context.getPackageManager()
+ .getResourcesForApplication(tile.getPackageName())
+ .getColor(colorRes, null /* theme */);
+ }
+ }
+ // If found anything, use it.
+ if (bgColor != 0) {
+ setBackgroundColor(bgColor);
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to set background color for " + tile.getPackageName());
+ }
+ setBackgroundColor(context.getColor(R.color.homepage_generic_icon_background));
+ }
+
+ /**
+ * Set background color by {@code color}
+ */
+ public void setBackgroundColor(int color) {
+ mBackgroundColor = color;
+ getDrawable(0).setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ Log.d(TAG, "Setting background color " + mBackgroundColor);
+ mAdaptiveConstantState.mColor = color;
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mAdaptiveConstantState;
+ }
+
+ @VisibleForTesting
+ static class AdaptiveConstantState extends ConstantState {
+ Context mContext;
+ Drawable mDrawable;
+ int mColor;
+
+ AdaptiveConstantState(Context context, Drawable drawable) {
+ this.mContext = context;
+ this.mDrawable = drawable;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ final AdaptiveIcon
+ icon = new AdaptiveIcon(mContext, mDrawable);
+ icon.setBackgroundColor(mColor);
+
+ return icon;
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
new file mode 100644
index 0000000..4d7610c
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.util.AttributeSet;
+import android.util.PathParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Draws a filled {@link ShapeDrawable} using the path from {@link AdaptiveIconDrawable}.
+ */
+public class AdaptiveIconShapeDrawable extends ShapeDrawable {
+ public AdaptiveIconShapeDrawable() {
+ super();
+ }
+
+ public AdaptiveIconShapeDrawable(Resources resources) {
+ super();
+ init(resources);
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+ init(r);
+ }
+
+ private void init(Resources resources) {
+ final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+ final Path path = new Path(PathParser.createPathFromPathData(
+ resources.getString(com.android.internal.R.string.config_icon_mask)));
+ setShape(new PathShape(path, pathSize, pathSize));
+ }
+}
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
new file mode 100644
index 0000000..1c65bc2
--- /dev/null
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.PathParser;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Adaptive outline drawable with white plain background color and black outline
+ */
+public class AdaptiveOutlineDrawable extends DrawableWrapper {
+ @VisibleForTesting
+ final Paint mOutlinePaint;
+ private Path mPath;
+ private final int mInsetPx;
+ private final Bitmap mBitmap;
+
+ public AdaptiveOutlineDrawable(Resources resources, Bitmap bitmap) {
+ super(new AdaptiveIconShapeDrawable(resources));
+
+ getDrawable().setTint(Color.WHITE);
+ mPath = new Path(PathParser.createPathFromPathData(
+ resources.getString(com.android.internal.R.string.config_icon_mask)));
+ mOutlinePaint = new Paint();
+ mOutlinePaint.setColor(resources.getColor(R.color.bt_outline_color, null));
+ mOutlinePaint.setStyle(Paint.Style.STROKE);
+ mOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.adaptive_outline_stroke));
+ mOutlinePaint.setAntiAlias(true);
+
+ mInsetPx = resources
+ .getDimensionPixelSize(R.dimen.dashboard_tile_foreground_image_inset);
+ mBitmap = bitmap;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ final Rect bounds = getBounds();
+ final float pathSize = AdaptiveIconDrawable.MASK_SIZE;
+
+ final float scaleX = (bounds.right - bounds.left) / pathSize;
+ final float scaleY = (bounds.bottom - bounds.top) / pathSize;
+
+ final int count = canvas.save();
+ canvas.scale(scaleX, scaleY);
+ // Draw outline
+ canvas.drawPath(mPath, mOutlinePaint);
+ canvas.restoreToCount(count);
+
+ // Draw the foreground icon
+ canvas.drawBitmap(mBitmap, bounds.left + mInsetPx, bounds.top + mInsetPx, null);
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mBitmap.getHeight() + 2 * mInsetPx;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mBitmap.getWidth() + 2 * mInsetPx;
+ }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 730e9e1..b532621 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -22,6 +22,7 @@
"SettingsLibEntityHeaderWidgets",
"SettingsLibBarChartPreference",
"SettingsLibProgressBar",
+ "SettingsLibAdaptiveIcon",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/Tile/Android.bp b/packages/SettingsLib/Tile/Android.bp
new file mode 100644
index 0000000..bf16ef3
--- /dev/null
+++ b/packages/SettingsLib/Tile/Android.bp
@@ -0,0 +1,11 @@
+android_library {
+ name: "SettingsLibTile",
+
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ ],
+
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/Tile/AndroidManifest.xml b/packages/SettingsLib/Tile/AndroidManifest.xml
new file mode 100644
index 0000000..b13532e
--- /dev/null
+++ b/packages/SettingsLib/Tile/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.drawer">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
similarity index 93%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
index a3dda65..7b062b1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * Copyright (C) 2019 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
+ * 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,
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.settingslib.drawer;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
@@ -26,6 +25,9 @@
import java.util.Collections;
import java.util.List;
+/**
+ * The category for handle {@link Tile}
+ */
public class DashboardCategory implements Parcelable {
/**
@@ -67,18 +69,30 @@
return result;
}
+ /**
+ * Add tile
+ */
public synchronized void addTile(Tile tile) {
mTiles.add(tile);
}
+ /**
+ * Remove tile
+ */
public synchronized void removeTile(int n) {
mTiles.remove(n);
}
+ /**
+ * Get size of tile
+ */
public int getTilesCount() {
return mTiles.size();
}
+ /**
+ * Get tile
+ */
public Tile getTile(int n) {
return mTiles.get(n);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
similarity index 95%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index d28b00a..5108efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
+/*
+ * Copyright (C) 2019 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
+ * 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,
@@ -84,8 +84,8 @@
mActivityPackage = in.readString();
mActivityName = in.readString();
mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
- final int N = in.readInt();
- for (int i = 0; i < N; i++) {
+ final int number = in.readInt();
+ for (int i = 0; i < number; i++) {
userHandle.add(UserHandle.CREATOR.createFromParcel(in));
}
mCategory = in.readString();
@@ -101,9 +101,9 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mActivityPackage);
dest.writeString(mActivityName);
- final int N = userHandle.size();
- dest.writeInt(N);
- for (int i = 0; i < N; i++) {
+ final int size = userHandle.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
userHandle.get(i).writeToParcel(dest, flags);
}
dest.writeString(mCategory);
@@ -151,6 +151,9 @@
}
}
+ /**
+ * Check whether title has order.
+ */
public boolean hasOrder() {
return mMetaData.containsKey(META_DATA_KEY_ORDER)
&& mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
@@ -262,6 +265,9 @@
}
}
+ /**
+ * Check whether title has key.
+ */
public boolean hasKey() {
return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
}
@@ -361,9 +367,12 @@
}
};
+ /**
+ * Check whether title is only have primary profile
+ */
public boolean isPrimaryProfileOnly() {
- String profile = mMetaData != null ?
- mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
+ String profile = mMetaData != null
+ ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
profile = (profile != null ? profile : PROFILE_ALL);
return TextUtils.equals(profile, PROFILE_PRIMARY);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
similarity index 98%
rename from packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index 91892ab..31925ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.settingslib.drawer;
@@ -39,6 +39,10 @@
import java.util.List;
import java.util.Map;
+/**
+ * Utils is a helper class that contains profile key, meta data, settings action
+ * and static methods for get icon or text from uri.
+ */
public class TileUtils {
private static final boolean DEBUG_TIMING = false;
diff --git a/packages/SettingsLib/res/drawable/ic_media_device.xml b/packages/SettingsLib/res/drawable/ic_media_device.xml
new file mode 100644
index 0000000..5a6aeb4
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_device.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#00000000"
+ android:fillAlpha=".1"
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0 0h24v24H0z" />
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21 3H3c-1.1 0-2 0.9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2 -0.9 2-2V5c0-1.1 -0.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index ed3c11c..39c55fd 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -603,4 +603,26 @@
<item>3</item><item>3</item>
</array>
+ <!-- Bluetooth icon foreground colors -->
+ <integer-array name="bt_icon_fg_colors">
+ <item>@color/bt_color_icon_1</item>
+ <item>@color/bt_color_icon_2</item>
+ <item>@color/bt_color_icon_3</item>
+ <item>@color/bt_color_icon_4</item>
+ <item>@color/bt_color_icon_5</item>
+ <item>@color/bt_color_icon_6</item>
+ <item>@color/bt_color_icon_7</item>
+ </integer-array>
+
+ <!-- Bluetooth icon background colors -->
+ <integer-array name="bt_icon_bg_colors">
+ <item>@color/bt_color_bg_1</item>
+ <item>@color/bt_color_bg_2</item>
+ <item>@color/bt_color_bg_3</item>
+ <item>@color/bt_color_bg_4</item>
+ <item>@color/bt_color_bg_5</item>
+ <item>@color/bt_color_bg_6</item>
+ <item>@color/bt_color_bg_7</item>
+ </integer-array>
+
</resources>
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index 66bbb3a..4b91bbb 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -19,4 +19,20 @@
<color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
<color name="list_divider_color">#64000000</color>
+
+ <color name="bt_color_icon_1">#48a50e0e</color> <!-- 72% Material Red 900 -->
+ <color name="bt_color_icon_2">#480d652d</color> <!-- 72% Material Green 900 -->
+ <color name="bt_color_icon_3">#48e37400</color> <!-- 72% Material Yellow 900 -->
+ <color name="bt_color_icon_4">#48b06000</color> <!-- 72% Material Orange 900 -->
+ <color name="bt_color_icon_5">#489c166b</color> <!-- 72% Material Pink 900 -->
+ <color name="bt_color_icon_6">#48681da8</color> <!-- 72% Material Purple 900 -->
+ <color name="bt_color_icon_7">#48007b83</color> <!-- 72% Material Cyan 900 -->
+
+ <color name="bt_color_bg_1">#fad2cf</color> <!-- Material Red 100 -->
+ <color name="bt_color_bg_2">#ceead6</color> <!-- Material Green 100 -->
+ <color name="bt_color_bg_3">#feefc3</color> <!-- Material Yellow 100 -->
+ <color name="bt_color_bg_4">#fedfc8</color> <!-- Material Orange 100 -->
+ <color name="bt_color_bg_5">#fdcfe8</color> <!-- Material Pink 100 -->
+ <color name="bt_color_bg_6">#e9d2fd</color> <!-- Material Purple 100 -->
+ <color name="bt_color_bg_7">#cbf0f8</color> <!-- Material Cyan 100 -->
</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index a9c5061..2cb9d4b 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -91,6 +91,7 @@
<!-- How far to inset the rounded edges -->
<dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
-
+ <!-- Size of nearby icon -->
+ <dimen name="bt_nearby_icon_size">24dp</dimen>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index bb8c8a6..867efb4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,18 +1,30 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.Log;
import android.util.Pair;
import androidx.annotation.DrawableRes;
import com.android.settingslib.R;
+import com.android.settingslib.widget.AdaptiveIcon;
+import com.android.settingslib.widget.AdaptiveOutlineDrawable;
+import java.io.IOException;
import java.util.List;
public class BluetoothUtils {
+ private static final String TAG = "BluetoothUtils";
+
public static final boolean V = false; // verbose logging
public static final boolean D = true; // regular logging
@@ -112,4 +124,68 @@
public static Drawable getBluetoothDrawable(Context context, @DrawableRes int resId) {
return context.getDrawable(resId);
}
+
+ /**
+ * Get colorful bluetooth icon with description
+ */
+ public static Pair<Drawable, String> getBtRainbowDrawableWithDescription(Context context,
+ CachedBluetoothDevice cachedDevice) {
+ final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription(
+ context, cachedDevice);
+ final BluetoothDevice bluetoothDevice = cachedDevice.getDevice();
+ final boolean untetheredHeadset = bluetoothDevice != null
+ ? Boolean.parseBoolean(bluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))
+ : false;
+ final int iconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.bt_nearby_icon_size);
+ final Resources resources = context.getResources();
+
+ // Deal with untethered headset
+ if (untetheredHeadset) {
+ final String uriString = bluetoothDevice != null
+ ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)
+ : null;
+ final Uri iconUri = uriString != null ? Uri.parse(uriString) : null;
+ if (iconUri != null) {
+ try {
+ final Bitmap bitmap = MediaStore.Images.Media.getBitmap(
+ context.getContentResolver(), iconUri);
+ if (bitmap != null) {
+ final Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, iconSize,
+ iconSize, false);
+ bitmap.recycle();
+ final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(
+ resources, resizedBitmap);
+ return new Pair<>(drawable, pair.second);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to get drawable for: " + iconUri, e);
+ }
+ }
+ }
+
+ return new Pair<>(buildBtRainbowDrawable(context,
+ pair.first, cachedDevice.getAddress().hashCode()), pair.second);
+ }
+
+ /**
+ * Build Bluetooth device icon with rainbow
+ */
+ public static Drawable buildBtRainbowDrawable(Context context, Drawable drawable,
+ int hashCode) {
+ final Resources resources = context.getResources();
+
+ // Deal with normal headset
+ final int[] iconFgColors = resources.getIntArray(R.array.bt_icon_fg_colors);
+ final int[] iconBgColors = resources.getIntArray(R.array.bt_icon_bg_colors);
+
+ // get color index based on mac address
+ final int index = Math.abs(hashCode % iconBgColors.length);
+ drawable.setColorFilter(iconFgColors[index], PorterDuff.Mode.SRC_ATOP);
+ final Drawable adaptiveIcon = new AdaptiveIcon(context, drawable);
+ ((AdaptiveIcon) adaptiveIcon).setBackgroundColor(iconBgColors[index]);
+
+ return adaptiveIcon;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3092b99..2711e31 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -18,8 +18,11 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.util.Pair;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
/**
@@ -48,9 +51,10 @@
}
@Override
- public int getIcon() {
- //TODO(b/117129183): This is not final icon for bluetooth device, just for demo.
- return com.android.internal.R.drawable.ic_bt_headphones_a2dp;
+ public Drawable getIcon() {
+ final Pair<Drawable, String> pair = BluetoothUtils
+ .getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+ return pair.first;
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 95f3d3d..732e8db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,10 +16,14 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.widget.Toast;
import androidx.mediarouter.media.MediaRouter;
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+
/**
* InfoMediaDevice extends MediaDevice to represents wifi device.
*/
@@ -46,9 +50,10 @@
}
@Override
- public int getIcon() {
- //TODO(b/121083246): This is not final icon for cast device, just for demo.
- return com.android.internal.R.drawable.ic_settings_print;
+ public Drawable getIcon() {
+ //TODO(b/120669861): Return remote device icon uri once api is ready.
+ return BluetoothUtils.buildBtRainbowDrawable(mContext,
+ mContext.getDrawable(R.drawable.ic_media_device), getId().hashCode());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 9b9e803..53a8520 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,6 +16,7 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import androidx.annotation.IntDef;
@@ -70,11 +71,11 @@
public abstract String getSummary();
/**
- * Get resource id of MediaDevice.
+ * Get icon of MediaDevice.
*
- * @return resource id of MediaDevice.
+ * @return drawable of icon.
*/
- public abstract int getIcon();
+ public abstract Drawable getIcon();
/**
* Get unique ID that represent MediaDevice
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 8c3fcc0..af91c34 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -16,10 +16,12 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.Log;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -56,8 +58,9 @@
}
@Override
- public int getIcon() {
- return R.drawable.ic_smartphone;
+ public Drawable getIcon() {
+ return BluetoothUtils.buildBtRainbowDrawable(mContext,
+ mContext.getDrawable(R.drawable.ic_smartphone), getId().hashCode());
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index fdc0fd3..5f2bc4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -84,7 +84,7 @@
private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS;
/** Maximum age of scan results to hold onto while actively scanning. **/
- private static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
+ @VisibleForTesting static final long MAX_SCAN_RESULT_AGE_MILLIS = 15000;
private static final String TAG = "WifiTracker";
private static final boolean DBG() {
@@ -142,6 +142,13 @@
*/
private boolean mStaleScanResults = true;
+ /**
+ * Tracks whether the latest SCAN_RESULTS_AVAILABLE_ACTION contained new scans. If not, then
+ * we treat the last scan as an aborted scan and increase the eviction timeout window to avoid
+ * completely flushing the AP list before the next successful scan completes.
+ */
+ private boolean mLastScanSucceeded = true;
+
// Does not need to be locked as it only updated on the worker thread, with the exception of
// during onStart, which occurs before the receiver is registered on the work handler.
private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
@@ -478,17 +485,22 @@
}
/**
- * Remove old scan results from the cache.
+ * Remove old scan results from the cache. If {@link #mLastScanSucceeded} is false, then
+ * increase the timeout window to avoid completely flushing the AP list before the next
+ * successful scan completes.
*
* <p>Should only ever be invoked from {@link #updateScanResultCache(List)} when
* {@link #mStaleScanResults} is false.
*/
private void evictOldScans() {
+ long evictionTimeoutMillis = mLastScanSucceeded ? MAX_SCAN_RESULT_AGE_MILLIS
+ : MAX_SCAN_RESULT_AGE_MILLIS * 2;
+
long nowMs = SystemClock.elapsedRealtime();
for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
ScanResult result = iter.next();
// result timestamp is in microseconds
- if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) {
+ if (nowMs - result.timestamp / 1000 > evictionTimeoutMillis) {
iter.remove();
}
}
@@ -840,6 +852,8 @@
WifiManager.WIFI_STATE_UNKNOWN));
} else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
mStaleScanResults = false;
+ mLastScanSucceeded =
+ intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, true);
fetchScansAndConfigsAndUpdateAccessPoints();
} else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index edf414d..683ec8b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -270,7 +270,7 @@
SystemClock.elapsedRealtime() * 1000 /* microsecond timestamp */);
}
- private static ScanResult buildStaleScanResult() {
+ private static ScanResult buildScanResultWithTimestamp(long timestampMillis) {
return new ScanResult(
WifiSsid.createFromAsciiEncoded(SSID_3),
BSSID_3,
@@ -280,7 +280,7 @@
"", // capabilities
RSSI_3,
0, // frequency
- 0 /* microsecond timestamp */);
+ timestampMillis * 1000 /* microsecond timestamp */);
}
private static WifiConfiguration buildPasspointConfiguration(String fqdn, String friendlyName) {
@@ -379,6 +379,12 @@
tracker.mReceiver.onReceive(mContext, i);
}
+ private void sendFailedScanResults(WifiTracker tracker) throws InterruptedException {
+ Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ i.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+ tracker.mReceiver.onReceive(mContext, i);
+ }
+
private void sendUpdatedScores() throws InterruptedException {
Bundle attr1 = new Bundle();
attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1);
@@ -982,8 +988,8 @@
@Test
public void onStart_updateScanResults_evictOldScanResult() {
- when(mockWifiManager.getScanResults()).thenReturn(
- Arrays.asList(buildScanResult1(), buildScanResult2(), buildStaleScanResult()));
+ when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList(
+ buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(0)));
WifiTracker tracker = createMockedWifiTracker();
tracker.forceUpdate();
@@ -995,6 +1001,33 @@
}
/**
+ * Verifies that a failed scan reported on SCAN_RESULTS_AVAILABLE_ACTION should increase the
+ * ScanResult eviction timeout to twice the default.
+ */
+ @Test
+ public void failedScan_increasesEvictionTimeout() throws InterruptedException {
+ when(mockWifiManager.getScanResults()).thenReturn(Arrays.asList(
+ buildScanResult1(), buildScanResult2(), buildScanResultWithTimestamp(
+ SystemClock.elapsedRealtime() - WifiTracker.MAX_SCAN_RESULT_AGE_MILLIS)));
+ WifiTracker tracker = createMockedWifiTracker();
+
+ sendFailedScanResults(tracker);
+
+ // Failed scan increases timeout window to include the stale scan
+ assertThat(tracker.getAccessPoints()).hasSize(3);
+ assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+ assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+ assertThat(tracker.getAccessPoints().get(2).getBssid()).isEqualTo(BSSID_3);
+
+ sendScanResults(tracker);
+
+ // Successful scan resets the timeout window to remove the stale scan
+ assertThat(tracker.getAccessPoints()).hasSize(2);
+ assertThat(tracker.getAccessPoints().get(0).getBssid()).isEqualTo(BSSID_1);
+ assertThat(tracker.getAccessPoints().get(1).getBssid()).isEqualTo(BSSID_2);
+ }
+
+ /**
* Verifies that updatePasspointAccessPoints will only return AccessPoints whose
* isPasspoint() evaluates as true.
*/
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index b713e08..b228cf7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,15 +15,20 @@
*/
package com.android.settingslib.bluetooth;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.Pair;
+import com.android.settingslib.widget.AdaptiveIcon;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +43,9 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+
private Context mContext;
@Before
@@ -66,4 +74,16 @@
verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_laptop);
}
+
+ @Test
+ public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false");
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb");
+
+ assertThat(BluetoothUtils.getBtRainbowDrawableWithDescription(
+ RuntimeEnvironment.application,
+ mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class);
+ }
}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
new file mode 100644
index 0000000..ed6b9b0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+
+import com.android.settingslib.R;
+import com.android.settingslib.drawer.CategoryKey;
+import com.android.settingslib.drawer.Tile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveIconTest {
+
+ private Context mContext;
+ private ActivityInfo mActivityInfo;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mActivityInfo = new ActivityInfo();
+ mActivityInfo.packageName = mContext.getPackageName();
+ mActivityInfo.name = "class";
+ mActivityInfo.metaData = new Bundle();
+ }
+
+ @Test
+ public void createIcon_shouldSetBackgroundAndInset() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ assertThat(icon.getNumberOfLayers()).isEqualTo(2);
+ assertThat(icon.getDrawable(0)).isInstanceOf(AdaptiveIconShapeDrawable.class);
+ }
+
+ @Test
+ public void setBackgroundColor_shouldUpdateColorFilter() {
+ final AdaptiveIcon icon =
+ spy(new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)));
+ final ShapeDrawable background = mock(ShapeDrawable.class);
+ when(icon.getDrawable(0)).thenReturn(background);
+
+ icon.setBackgroundColor(Color.BLUE);
+
+ verify(background).setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ @Test
+ public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+ assertThat(icon.mBackgroundColor).isEqualTo(0xff0000);
+ }
+
+ @Test
+ public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+ final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.homepage_generic_icon_background));
+ }
+
+ @Test
+ public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() {
+ final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE));
+ mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT,
+ R.color.bt_outline_color);
+ doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update))
+ .when(tile).getIcon(mContext);
+
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(mContext, tile);
+
+ assertThat(icon.mBackgroundColor)
+ .isEqualTo(mContext.getColor(R.color.bt_outline_color));
+ }
+
+ @Test
+ public void getConstantState_returnCorrectState() {
+ final AdaptiveIcon icon =
+ new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK));
+ icon.setBackgroundColor(Color.YELLOW);
+
+ final AdaptiveIcon.AdaptiveConstantState state =
+ (AdaptiveIcon.AdaptiveConstantState) icon.getConstantState();
+
+ assertThat(state.mColor).isEqualTo(Color.YELLOW);
+ assertThat(state.mContext).isEqualTo(mContext);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
new file mode 100644
index 0000000..71d55bc
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveOutlineDrawableTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Paint;
+
+import com.android.settingslib.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class AdaptiveOutlineDrawableTest {
+
+ @Test
+ public void constructor_initPaint() {
+ final Resources resources = RuntimeEnvironment.application.getResources();
+ final AdaptiveOutlineDrawable drawable = new AdaptiveOutlineDrawable(resources, null);
+
+ assertThat(drawable.mOutlinePaint.getStyle()).isEqualTo(Paint.Style.STROKE);
+ assertThat(drawable.mOutlinePaint.getStrokeWidth()).isWithin(0.01f).of(
+ resources.getDimension(R.dimen.adaptive_outline_stroke));
+ }
+
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d39646b..2a9456d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -168,6 +168,9 @@
<uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
+ <!-- Permission needed to invoke DynamicSystem (AOT) -->
+ <uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
+
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
index 7b7657a..105be46 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
@@ -28,7 +28,7 @@
public interface ClockPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_CLOCK";
- int VERSION = 2;
+ int VERSION = 3;
/**
* Get the name of the clock face.
@@ -48,6 +48,17 @@
Bitmap getThumbnail();
/**
+ * Get preview images of clock face to be shown in the picker app.
+ *
+ * Preview image should be realistic and show what the clock face will look like on AOD and lock
+ * screen.
+ *
+ * @param width width of the preview image, should be the same as device width in pixels.
+ * @param height height of the preview image, should be the same as device height in pixels.
+ */
+ Bitmap getPreview(int width, int height);
+
+ /**
* Get clock view.
* @return clock view from plugin.
*/
diff --git a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml
deleted file mode 100644
index b20e158..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_branded_vpn.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="12.0dp"
- android:height="12.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_vpn.xml b/packages/SystemUI/res/drawable/ic_qs_vpn.xml
deleted file mode 100644
index 6567d12..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_vpn.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="12.0dp"
- android:height="12.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M12.09,9C11.11,7.5 9.43,6.5 7.5,6.5C4.46,6.5 2,8.96 2,12c0,3.04 2.46,5.5 5.5,5.5c1.93,0 3.61,-1 4.59,-2.5H14v3h6v-3h2V9H12.09zM20,13h-2v3h-2v-3h-5.16c-0.43,1.44 -1.76,2.5 -3.34,2.5C5.57,15.5 4,13.93 4,12c0,-1.93 1.57,-3.5 3.5,-3.5c1.58,0 2.9,1.06 3.34,2.5H20V13z"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M7.5,12m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
index bfae857..5913cdf 100644
--- a/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_branded_vpn.xml
@@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<!-- This icon is statically overlayed - do not remove.-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17.0dp"
android:height="17.0dp"
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 9acfbaf..c420117 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -54,6 +54,8 @@
android:background="@drawable/rounded_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:scaleType="fitCenter"
+ android:padding="@dimen/volume_dialog_ringer_icon_padding"
android:tint="@color/accent_tint_color_selector"
android:layout_gravity="center"
android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
deleted file mode 100644
index 665fc3f..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport ="true"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/dialog_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
- android:textColor="@*android:color/text_color_primary"
- android:layout_marginStart="@dimen/ongoing_appops_dialog_title_margin_sides"
- android:layout_marginEnd="@dimen/ongoing_appops_dialog_title_margin_sides"
- android:layout_marginBottom="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
- android:layout_marginTop="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
- />
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/ongoing_appops_dialog_items_bottom_margin" >
-
- <LinearLayout
- android:id="@+id/items_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="start"
- />
- </LinearLayout>
-
- </LinearLayout>
-
-</ScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
deleted file mode 100644
index c8e0845..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="@dimen/ongoing_appops_dialog_line_height"
- android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
- android:layout_marginEnd="@dimen/ongoing_appops_dialog_text_padding"
- android:fillViewport="true"
- android:orientation="horizontal"
- android:focusable="true"
- android:layout_gravity="center_vertical">
-
- <FrameLayout
- android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
- android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
- android:layout_gravity="start|center_vertical">
-
- <ImageView
- android:id="@+id/app_icon"
- android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
- android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
- android:layout_gravity="center"
- />
- </FrameLayout>
-
- <TextView
- android:id="@+id/app_name"
- android:layout_height="match_parent"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:gravity="start|center_vertical"
- android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
- android:textColor="@*android:color/text_color_primary"
- android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
- />
-
- <LinearLayout
- android:id="@+id/icons"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:gravity="end"
- android:layout_gravity="end|center_vertical"
- android:visibility="gone"
- />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index d1c80c4..a90b1eb 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -56,6 +56,8 @@
android:background="@drawable/rounded_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:scaleType="fitCenter"
+ android:padding="@dimen/volume_dialog_ringer_icon_padding"
android:tint="@color/accent_tint_color_selector"
android:layout_gravity="center"
android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml
deleted file mode 100644
index 2c549bc..0000000
--- a/packages/SystemUI/res/values/config_car.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, 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.
-*/
--->
-
-<resources>
- <!-- These next two work together, if you enable this first one you need to provide an intent
- uri that will be launched into the docked window. -->
- <bool name="config_enablePersistentDockedActivity">false</bool>
- <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
-
- <!-- configure which system ui bars should be displayed -->
- <bool name="config_enableLeftNavigationBar">false</bool>
- <bool name="config_enableRightNavigationBar">false</bool>
- <bool name="config_enableBottomNavigationBar">true</bool>
- <bool name="config_hideNavWhenKeyguardBouncerShown">true</bool>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 039eca6..1a865ee 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -341,6 +341,8 @@
<dimen name="volume_dialog_ringer_size">64dp</dimen>
+ <dimen name="volume_dialog_ringer_icon_padding">20dp</dimen>
+
<dimen name="volume_dialog_caption_size">64dp</dimen>
<dimen name="volume_dialog_tap_target_size">48dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 03b6a52..0411d01 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2345,18 +2345,6 @@
<!-- Content description for ongoing privacy chip. Use with multiple apps [CHAR LIMIT=NONE]-->
<string name="ongoing_privacy_chip_content_multiple_apps">Applications are using your <xliff:g id="types_list" example="camera, location">%s</xliff:g>.</string>
- <!-- Action for accepting the Ongoing privacy dialog [CHAR LIMIT=10]-->
- <string name="ongoing_privacy_dialog_ok">Got it</string>
-
- <!-- Action on Ongoing Privacy Dialog to open privacy hub [CHAR LIMIT=23]-->
- <string name="ongoing_privacy_dialog_open_settings">Privacy settings</string>
-
- <!-- Text for item in Ongoing Privacy Dialog title when only one app is using app ops [CHAR LIMIT=NONE] -->
- <string name="ongoing_privacy_dialog_single_app_title">App using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
-
- <!-- Text for item in Ongoing Privacy Dialog title when multiple apps is using app ops [CHAR LIMIT=NONE] -->
- <string name="ongoing_privacy_dialog_multiple_apps_title">Apps using your <xliff:g id="types_list" example="camera( and location)">%s</xliff:g></string>
-
<!-- Separator for types. Include spaces before and after if needed [CHAR LIMIT=10] -->
<string name="ongoing_privacy_dialog_separator">,\u0020</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c0ec405..fb4fe81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -99,13 +99,7 @@
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
}
- private void showDefaultMessage() {
- if (mRemainingAttempts >= 0) {
- mSecurityMessageDisplay.setMessage(getPinPasswordErrorMessage(
- mRemainingAttempts, true));
- return;
- }
-
+ private void setLockedSimMessage() {
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
int count = TelephonyManager.getDefault().getSimCount();
Resources rez = getResources();
@@ -122,13 +116,20 @@
color = info.getIconTint();
}
}
-
if (isEsimLocked) {
msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
}
mSecurityMessageDisplay.setMessage(msg);
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private void showDefaultMessage() {
+ setLockedSimMessage();
+ if (mRemainingAttempts >= 0) {
+ return;
+ }
+
// Sending empty PIN here to query the number of remaining PIN attempts
new CheckSimPin("", mSubId) {
@@ -137,8 +138,7 @@
" attemptsRemaining=" + attemptsRemaining);
if (attemptsRemaining >= 0) {
mRemainingAttempts = attemptsRemaining;
- mSecurityMessageDisplay.setMessage(
- getPinPasswordErrorMessage(attemptsRemaining, true));
+ setLockedSimMessage();
}
}
}.start();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
index 32c1242..147def39 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java
@@ -15,15 +15,19 @@
*/
package com.android.keyguard.clock;
+import android.app.WallpaperManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.Paint.Style;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextClock;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import java.util.TimeZone;
@@ -44,6 +48,16 @@
private final LayoutInflater mLayoutInflater;
/**
+ * Extracts accent color from wallpaper.
+ */
+ private final SysuiColorExtractor mColorExtractor;
+
+ /**
+ * Renders preview from clock view.
+ */
+ private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+ /**
* Custom clock shown on AOD screen and behind stack scroller on lock.
*/
private View mView;
@@ -64,11 +78,15 @@
/**
* Create a BubbleClockController instance.
*
- * @param layoutInflater Inflater used to inflate custom clock views.
+ * @param res Resources contains title and thumbnail.
+ * @param inflater Inflater used to inflate custom clock views.
+ * @param colorExtractor Extracts accent color from wallpaper.
*/
- public BubbleClockController(Resources res, LayoutInflater inflater) {
+ public BubbleClockController(Resources res, LayoutInflater inflater,
+ SysuiColorExtractor colorExtractor) {
mResources = res;
mLayoutInflater = inflater;
+ mColorExtractor = colorExtractor;
}
private void createViews() {
@@ -99,6 +117,23 @@
}
@Override
+ public Bitmap getPreview(int width, int height) {
+
+ // Use the big clock view for the preview
+ View view = getBigClockView();
+
+ // Initialize state of plugin before generating preview.
+ setDarkAmount(1f);
+ setTextColor(Color.WHITE);
+ ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+ WallpaperManager.FLAG_LOCK, true);
+ setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+ onTimeTick();
+
+ return mRenderer.createPreview(view, width, height);
+ }
+
+ @Override
public View getView() {
if (mLockClockContainer == null) {
createViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 8ad5c7b..d0fff74 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -16,29 +16,19 @@
package com.android.keyguard.clock;
import android.annotation.Nullable;
-import android.app.WallpaperManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dock.DockManager;
@@ -51,8 +41,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -128,7 +116,6 @@
private final List<ClockChangedListener> mListeners = new ArrayList<>();
- private final SysuiColorExtractor mColorExtractor;
private final int mWidth;
private final int mHeight;
@@ -144,17 +131,16 @@
ContentResolver contentResolver, SettingsWrapper settingsWrapper) {
mContext = context;
mPluginManager = pluginManager;
- mColorExtractor = colorExtractor;
mContentResolver = contentResolver;
mSettingsWrapper = settingsWrapper;
Resources res = context.getResources();
LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
- addClockPlugin(new DefaultClockController(res, layoutInflater));
- addClockPlugin(new BubbleClockController(res, layoutInflater));
- addClockPlugin(new StretchAnalogClockController(res, layoutInflater));
- addClockPlugin(new TypeClockController(res, layoutInflater));
+ addClockPlugin(new DefaultClockController(res, layoutInflater, colorExtractor));
+ addClockPlugin(new BubbleClockController(res, layoutInflater, colorExtractor));
+ addClockPlugin(new StretchAnalogClockController(res, layoutInflater, colorExtractor));
+ addClockPlugin(new TypeClockController(res, layoutInflater, colorExtractor));
// Store the size of the display for generation of clock preview.
DisplayMetrics dm = res.getDisplayMetrics();
@@ -217,7 +203,7 @@
.setTitle(plugin.getTitle())
.setId(id)
.setThumbnail(() -> plugin.getThumbnail())
- .setPreview(() -> getClockPreview(id))
+ .setPreview(() -> plugin.getPreview(mWidth, mHeight))
.build());
}
@@ -232,81 +218,6 @@
}
}
- /**
- * Generate a realistic preview of a clock face.
- * @param clockId ID of clock to use for preview, should be obtained from {@link getClockInfos}.
- * Returns null if clockId is not found.
- */
- @Nullable
- private Bitmap getClockPreview(String clockId) {
- FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
- @Override
- public Bitmap call() {
- Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888);
- ClockPlugin plugin = mClocks.get(clockId);
- if (plugin == null) {
- return null;
- }
-
- // Use the big clock view for the preview
- View clockView = plugin.getBigClockView();
- if (clockView == null) {
- return null;
- }
-
- // Initialize state of plugin before generating preview.
- plugin.setDarkAmount(1f);
- plugin.setTextColor(Color.WHITE);
-
- ColorExtractor.GradientColors colors = mColorExtractor.getColors(
- WallpaperManager.FLAG_LOCK, true);
- plugin.setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
- plugin.onTimeTick();
-
- // Draw clock view hierarchy to canvas.
- Canvas canvas = new Canvas(bitmap);
- canvas.drawColor(Color.BLACK);
- dispatchVisibilityAggregated(clockView, true);
- clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));
- clockView.layout(0, 0, mWidth, mHeight);
- clockView.draw(canvas);
- return bitmap;
- }
- });
-
- if (Looper.myLooper() == Looper.getMainLooper()) {
- task.run();
- } else {
- mMainHandler.post(task);
- }
-
- try {
- return task.get();
- } catch (Exception e) {
- Log.e(TAG, "Error completing task", e);
- return null;
- }
- }
-
- private void dispatchVisibilityAggregated(View view, boolean isVisible) {
- // Similar to View.dispatchVisibilityAggregated implementation.
- final boolean thisVisible = view.getVisibility() == View.VISIBLE;
- if (thisVisible || !isVisible) {
- view.onVisibilityAggregated(isVisible);
- }
-
- if (view instanceof ViewGroup) {
- isVisible = thisVisible && isVisible;
- ViewGroup vg = (ViewGroup) view;
- int count = vg.getChildCount();
-
- for (int i = 0; i < count; i++) {
- dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
- }
- }
- }
-
private void notifyClockChanged(ClockPlugin plugin) {
for (int i = 0; i < mListeners.size(); i++) {
// It probably doesn't make sense to supply the same plugin instances to multiple
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
index 8a6a4cd..73414b3 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java
@@ -15,15 +15,19 @@
*/
package com.android.keyguard.clock;
+import android.app.WallpaperManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.Paint.Style;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import java.util.TimeZone;
@@ -44,6 +48,16 @@
private final LayoutInflater mLayoutInflater;
/**
+ * Extracts accent color from wallpaper.
+ */
+ private final SysuiColorExtractor mColorExtractor;
+
+ /**
+ * Renders preview from clock view.
+ */
+ private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+ /**
* Root view of preview.
*/
private View mView;
@@ -61,11 +75,15 @@
/**
* Create a DefaultClockController instance.
*
+ * @param res Resources contains title and thumbnail.
* @param inflater Inflater used to inflate custom clock views.
+ * @param colorExtractor Extracts accent color from wallpaper.
*/
- public DefaultClockController(Resources res, LayoutInflater inflater) {
+ public DefaultClockController(Resources res, LayoutInflater inflater,
+ SysuiColorExtractor colorExtractor) {
mResources = res;
mLayoutInflater = inflater;
+ mColorExtractor = colorExtractor;
}
private void createViews() {
@@ -90,6 +108,23 @@
}
@Override
+ public Bitmap getPreview(int width, int height) {
+
+ // Use the big clock view for the preview
+ View view = getBigClockView();
+
+ // Initialize state of plugin before generating preview.
+ setDarkAmount(1f);
+ setTextColor(Color.WHITE);
+ ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+ WallpaperManager.FLAG_LOCK, true);
+ setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+ onTimeTick();
+
+ return mRenderer.createPreview(view, width, height);
+ }
+
+ @Override
public View getView() {
return null;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
index 34b2fd8..ea9f0cd 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/StretchAnalogClockController.java
@@ -15,15 +15,19 @@
*/
package com.android.keyguard.clock;
+import android.app.WallpaperManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.Paint.Style;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextClock;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import java.util.TimeZone;
@@ -44,6 +48,16 @@
private final LayoutInflater mLayoutInflater;
/**
+ * Extracts accent color from wallpaper.
+ */
+ private final SysuiColorExtractor mColorExtractor;
+
+ /**
+ * Renders preview from clock view.
+ */
+ private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+ /**
* Custom clock shown on AOD screen and behind stack scroller on lock.
*/
private View mBigClockView;
@@ -64,11 +78,15 @@
/**
* Create a BubbleClockController instance.
*
- * @param layoutInflater Inflater used to inflate custom clock views.
+ * @param res Resources contains title and thumbnail.
+ * @param inflater Inflater used to inflate custom clock views.
+ * @param colorExtractor Extracts accent color from wallpaper.
*/
- public StretchAnalogClockController(Resources res, LayoutInflater inflater) {
+ public StretchAnalogClockController(Resources res, LayoutInflater inflater,
+ SysuiColorExtractor colorExtractor) {
mResources = res;
mLayoutInflater = inflater;
+ mColorExtractor = colorExtractor;
}
private void createViews() {
@@ -99,6 +117,23 @@
}
@Override
+ public Bitmap getPreview(int width, int height) {
+
+ // Use the big clock view for the preview
+ View view = getBigClockView();
+
+ // Initialize state of plugin before generating preview.
+ setDarkAmount(1f);
+ setTextColor(Color.WHITE);
+ ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+ WallpaperManager.FLAG_LOCK, true);
+ setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+ onTimeTick();
+
+ return mRenderer.createPreview(view, width, height);
+ }
+
+ @Override
public View getView() {
if (mView == null) {
createViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
index 387f265..67c0989 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java
@@ -15,14 +15,18 @@
*/
package com.android.keyguard.clock;
+import android.app.WallpaperManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.graphics.Paint.Style;
import android.view.LayoutInflater;
import android.view.View;
+import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.R;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import java.util.TimeZone;
@@ -43,6 +47,16 @@
private final LayoutInflater mLayoutInflater;
/**
+ * Extracts accent color from wallpaper.
+ */
+ private final SysuiColorExtractor mColorExtractor;
+
+ /**
+ * Renders preview from clock view.
+ */
+ private final ViewPreviewer mRenderer = new ViewPreviewer();
+
+ /**
* Custom clock shown on AOD screen and behind stack scroller on lock.
*/
private View mView;
@@ -61,11 +75,15 @@
/**
* Create a TypeClockController instance.
*
+ * @param res Resources contains title and thumbnail.
* @param inflater Inflater used to inflate custom clock views.
+ * @param colorExtractor Extracts accent color from wallpaper.
*/
- TypeClockController(Resources res, LayoutInflater inflater) {
+ TypeClockController(Resources res, LayoutInflater inflater,
+ SysuiColorExtractor colorExtractor) {
mResources = res;
mLayoutInflater = inflater;
+ mColorExtractor = colorExtractor;
}
private void createViews() {
@@ -96,6 +114,23 @@
}
@Override
+ public Bitmap getPreview(int width, int height) {
+
+ // Use the big clock view for the preview
+ View view = getBigClockView();
+
+ // Initialize state of plugin before generating preview.
+ setDarkAmount(1f);
+ setTextColor(Color.WHITE);
+ ColorExtractor.GradientColors colors = mColorExtractor.getColors(
+ WallpaperManager.FLAG_LOCK, true);
+ setColorPalette(colors.supportsDarkText(), colors.getColorPalette());
+ onTimeTick();
+
+ return mRenderer.createPreview(view, width, height);
+ }
+
+ @Override
public View getView() {
if (mLockClock == null) {
createViews();
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java
new file mode 100644
index 0000000..abd0dd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face.
+ */
+final class ViewPreviewer {
+
+ private static final String TAG = "ViewPreviewer";
+
+ /**
+ * Handler used to run {@link View#draw(Canvas)} on the main thread.
+ */
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ /**
+ * Generate a realistic preview of a clock face.
+ *
+ * @param view view is used to generate preview image.
+ * @param width width of the preview image, should be the same as device width in pixels.
+ * @param height height of the preview image, should be the same as device height in pixels.
+ * @return bitmap of view.
+ */
+ @Nullable
+ Bitmap createPreview(View view, int width, int height) {
+ if (view == null) {
+ return null;
+ }
+ FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
+ @Override
+ public Bitmap call() {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ // Draw clock view hierarchy to canvas.
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawColor(Color.BLACK);
+ dispatchVisibilityAggregated(view, true);
+ view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+ view.layout(0, 0, width, height);
+ view.draw(canvas);
+
+ return bitmap;
+ }
+ });
+
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ task.run();
+ } else {
+ mMainHandler.post(task);
+ }
+
+ try {
+ return task.get();
+ } catch (Exception e) {
+ Log.e(TAG, "Error completing task", e);
+ return null;
+ }
+ }
+
+ private void dispatchVisibilityAggregated(View view, boolean isVisible) {
+ // Similar to View.dispatchVisibilityAggregated implementation.
+ final boolean thisVisible = view.getVisibility() == View.VISIBLE;
+ if (thisVisible || !isVisible) {
+ view.onVisibilityAggregated(isVisible);
+ }
+
+ if (view instanceof ViewGroup) {
+ isVisible = thisVisible && isVisible;
+ ViewGroup vg = (ViewGroup) view;
+ int count = vg.getChildCount();
+
+ for (int i = 0; i < count; i++) {
+ dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 559c9f6..de887ff 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -381,10 +381,18 @@
}
/**
- * Update bubble expanded view header when user toggles dark mode.
+ * Update header color when user toggles dark mode.
*/
void updateHeaderColor() {
- mHeaderView.setBackgroundColor(mContext.getColor(R.attr.colorAccent));
+ TypedArray ta = mContext.obtainStyledAttributes(
+ new int[] {android.R.attr.colorBackgroundFloating, android.R.attr.colorForeground});
+ int bgColor = ta.getColor(0, Color.WHITE /* default */);
+ int btnColor = ta.getColor(1, Color.BLACK /* default */);
+ ta.recycle();
+
+ mHeaderView.setBackgroundColor(bgColor);
+ mSettingsIcon.setColorFilter(btnColor);
+ mDeepLinkIcon.setColorFilter(btnColor);
}
private void updateHeaderView() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 617090a..be55829 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -52,6 +52,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.bubbles.animation.ExpandedAnimationController;
@@ -270,8 +271,8 @@
* Handle config changes.
*/
public void onConfigChanged() {
- if (mExpandedBubble != null) {
- mExpandedBubble.expandedView.updateHeaderColor();
+ for (Bubble b: mBubbleData.getBubbles()) {
+ b.expandedView.updateHeaderColor();
}
}
@@ -316,7 +317,8 @@
}
switch (action) {
case AccessibilityNodeInfo.ACTION_DISMISS:
- stackDismissed(BubbleController.DISMISS_ACCESSIBILITY_ACTION);
+ Dependency.get(BubbleController.class).dismissStack(
+ BubbleController.DISMISS_ACCESSIBILITY_ACTION);
return true;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
collapseStack();
@@ -422,7 +424,7 @@
* Sets the entry that should be expanded and expands if needed.
*/
@VisibleForTesting
- public void setExpandedBubble(NotificationEntry entry) {
+ void setExpandedBubble(NotificationEntry entry) {
for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
if (entry.equals(bv.getEntry())) {
@@ -436,7 +438,7 @@
*
* @param entry the notification to add to the stack of bubbles.
*/
- public void addBubble(NotificationEntry entry) {
+ void addBubble(NotificationEntry entry) {
Bubble b = new Bubble(entry, mInflater, this /* stackView */, mBlockedListener);
mBubbleData.addBubble(b);
@@ -451,12 +453,17 @@
/**
* Remove a bubble from the stack.
*/
- public void removeBubble(String key, int reason) {
+ void removeBubble(String key, int reason) {
Bubble b = mBubbleData.removeBubble(key);
if (b == null) {
return;
}
- int removedIndex = dismissBubble(b, reason);
+ setBubbleDismissed(b, reason);
+
+ // Remove it from the views
+ int removedIndex = mBubbleContainer.indexOfChild(b.iconView);
+ mBubbleContainer.removeViewAt(removedIndex);
+
int bubbleCount = mBubbleContainer.getChildCount();
if (bubbleCount == 0) {
// If no bubbles remain, collapse the entire stack.
@@ -481,9 +488,9 @@
/**
* Dismiss the stack of bubbles.
*/
- public void stackDismissed(int reason) {
+ void stackDismissed(int reason) {
for (Bubble bubble : mBubbleData.getBubbles()) {
- dismissBubble(bubble, reason);
+ setBubbleDismissed(bubble, reason);
}
mBubbleData.clear();
collapseStack();
@@ -495,8 +502,7 @@
}
/**
- * Marks the notification entry as dismissed, cleans up Bubble icon and expanded view UI
- * elements and calls deleteIntent if necessary.
+ * Marks the notification entry as dismissed & calls any delete intents for the bubble.
*
* <p>Note: This does not remove the Bubble from BubbleData.
*
@@ -504,17 +510,13 @@
* @param reason code for the reason the dismiss was triggered
* @see BubbleController.DismissReason
*/
- private int dismissBubble(Bubble bubble, @DismissReason int reason) {
+ private void setBubbleDismissed(Bubble bubble, @DismissReason int reason) {
if (DEBUG) {
Log.d(TAG, "dismissBubble: " + bubble + " reason=" + reason);
}
bubble.entry.setBubbleDismissed(true);
bubble.expandedView.cleanUpExpandedState();
- // Remove it from the views
- int removedIndex = mBubbleContainer.indexOfChild(bubble.iconView);
- mBubbleContainer.removeViewAt(removedIndex);
-
if (reason == BubbleController.DISMISS_USER_GESTURE) {
Notification.BubbleMetadata bubbleMetadata = bubble.entry.getBubbleMetadata();
PendingIntent deleteIntent = bubbleMetadata != null
@@ -529,7 +531,6 @@
}
}
}
- return removedIndex;
}
/**
@@ -856,7 +857,6 @@
private void applyCurrentState() {
Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
-
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (mIsExpanded) {
// First update the view so that it calculates a new height (ensuring the y position
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 1dd31010..bd6882c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -56,6 +56,7 @@
private boolean mRegistered;
private int mDefaultDozeBrightness;
private boolean mPaused = false;
+ private boolean mScreenOff = false;
private int mLastSensorValue = -1;
/**
@@ -118,6 +119,7 @@
break;
}
if (newState != DozeMachine.State.FINISH) {
+ setScreenOff(newState == DozeMachine.State.DOZE);
setPaused(newState == DozeMachine.State.DOZE_AOD_PAUSED);
}
}
@@ -135,15 +137,15 @@
try {
if (mRegistered) {
mLastSensorValue = (int) event.values[0];
- updateBrightnessAndReady();
+ updateBrightnessAndReady(false /* force */);
}
} finally {
Trace.endSection();
}
}
- private void updateBrightnessAndReady() {
- if (mRegistered || mDebugBrightnessBucket != -1) {
+ private void updateBrightnessAndReady(boolean force) {
+ if (force || mRegistered || mDebugBrightnessBucket != -1) {
int sensorValue = mDebugBrightnessBucket == -1
? mLastSensorValue : mDebugBrightnessBucket;
int brightness = computeBrightness(sensorValue);
@@ -153,7 +155,7 @@
}
int scrimOpacity = -1;
- if (mPaused) {
+ if (mPaused || mScreenOff) {
// If AOD is paused, force the screen black until the
// sensor reports a new brightness. This ensures that when the screen comes on
// again, it will only show after the brightness sensor has stabilized,
@@ -216,13 +218,20 @@
private void setPaused(boolean paused) {
if (mPaused != paused) {
mPaused = paused;
- updateBrightnessAndReady();
+ updateBrightnessAndReady(false /* force */);
+ }
+ }
+
+ private void setScreenOff(boolean screenOff) {
+ if (mScreenOff != screenOff) {
+ mScreenOff = screenOff;
+ updateBrightnessAndReady(true /* force */);
}
}
@Override
public void onReceive(Context context, Intent intent) {
mDebugBrightnessBucket = intent.getIntExtra(BRIGHTNESS_BUCKET, -1);
- updateBrightnessAndReady();
+ updateBrightnessAndReady(false /* force */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
index 59b3c34..d08a373 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
@@ -53,9 +53,4 @@
else -> types.map { it.getName(context) }.joinWithAnd().toString()
}
}
-
- fun getDialogTitle(): String {
- return context.getString(R.string.ongoing_privacy_dialog_multiple_apps_title,
- joinTypes())
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 7c937a9..b682cb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -160,9 +160,9 @@
int footerIconId = R.drawable.ic_info_outline;
if (vpnName != null || vpnNameWorkProfile != null) {
if (mSecurityController.isVpnBranded()) {
- footerIconId = R.drawable.ic_qs_branded_vpn;
+ footerIconId = R.drawable.stat_sys_branded_vpn;
} else {
- footerIconId = R.drawable.ic_qs_vpn;
+ footerIconId = R.drawable.stat_sys_vpn_ic;
}
}
if (mFooterIconId != footerIconId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 7905617..211a40a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -70,7 +70,7 @@
private final String mTag;
private final View mView;
- private final BarBackgroundDrawable mBarBackground;
+ protected final BarBackgroundDrawable mBarBackground;
private int mMode;
private boolean mAlwaysOpaque = false;
@@ -152,7 +152,7 @@
return mode == MODE_LIGHTS_OUT || mode == MODE_LIGHTS_OUT_TRANSPARENT;
}
- private static class BarBackgroundDrawable extends Drawable {
+ protected static class BarBackgroundDrawable extends Drawable {
private final int mOpaque;
private final int mSemiTransparent;
private final int mTransparent;
@@ -171,6 +171,7 @@
private int mGradientAlphaStart;
private int mColorStart;
+ private Rect mFrame;
public BarBackgroundDrawable(Context context, int gradientResourceId) {
@@ -190,6 +191,10 @@
mGradient = context.getDrawable(gradientResourceId);
}
+ public void setFrame(Rect frame) {
+ mFrame = frame;
+ }
+
@Override
public void setAlpha(int alpha) {
// noop
@@ -296,7 +301,11 @@
if (mTintFilter != null) {
mPaint.setColorFilter(mTintFilter);
}
- canvas.drawPaint(mPaint);
+ if (mFrame != null) {
+ canvas.drawRect(mFrame, mPaint);
+ } else {
+ canvas.drawPaint(mPaint);
+ }
}
if (mAnimating) {
invalidateSelf(); // keep going
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index cbb5d54..9485623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -988,11 +988,11 @@
if (Intent.ACTION_SCREEN_ON.equals(action)) {
// Enabled and screen is on, start it again if enabled
if (NavBarTintController.isEnabled(getContext())) {
- mNavigationBarView.getColorAdaptionController().start();
+ mNavigationBarView.getTintController().start();
}
} else {
// Screen off disable it
- mNavigationBarView.getColorAdaptionController().stop();
+ mNavigationBarView.getTintController().stop();
}
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 3984405..d4cec42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -105,6 +106,10 @@
applyLightsOut(true, false);
}
+ void setBackgroundFrame(Rect frame) {
+ mBarBackground.setFrame(frame);
+ }
+
@Override
protected boolean isLightsOut(int mode) {
return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
@@ -119,6 +124,7 @@
protected void onTransition(int oldMode, int newMode, boolean animate) {
super.onTransition(oldMode, newMode, animate);
applyLightsOut(animate, false /*force*/);
+ mView.onBarTransition(newMode);
}
private void applyLightsOut(boolean animate, boolean force) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f2d6241..f22ecf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -30,6 +30,7 @@
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.NavigationBarInflaterView.NAV_BAR_VIEWS;
import android.animation.LayoutTransition;
@@ -172,7 +173,7 @@
private RecentsOnboarding mRecentsOnboarding;
private NotificationPanelView mPanelView;
- private NavBarTintController mColorAdaptionController;
+ private NavBarTintController mTintController;
private boolean mAssistantAvailable;
private NavigationPrototypeController mPrototypeController;
private NavigationGestureAction[] mDefaultGestureMap;
@@ -309,9 +310,9 @@
@Override
public void onColorAdaptChanged(boolean enabled) {
if (enabled) {
- mColorAdaptionController.start();
+ mTintController.start();
} else {
- mColorAdaptionController.stop();
+ mTintController.stop();
}
}
@@ -442,15 +443,15 @@
mPrototypeController = new NavigationPrototypeController(context);
mPrototypeController.register();
mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
- mColorAdaptionController = new NavBarTintController(this, getLightTransitionsController());
+ mTintController = new NavBarTintController(this, getLightTransitionsController());
IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
filter.addDataScheme("package");
context.registerReceiver(mOverlaysChangedReceiver, filter);
}
- public NavBarTintController getColorAdaptionController() {
- return mColorAdaptionController;
+ public NavBarTintController getTintController() {
+ return mTintController;
}
public BarTransitions getBarTransitions() {
@@ -476,7 +477,7 @@
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
- mColorAdaptionController.onDraw();
+ mTintController.onDraw();
}
private void updateNavigationGestures() {
@@ -557,6 +558,17 @@
return super.onTouchEvent(event);
}
+ void onBarTransition(int newMode) {
+ if (newMode == MODE_OPAQUE) {
+ // If the nav bar background is opaque, stop auto tinting since we know the icons are
+ // showing over a dark background
+ mTintController.stop();
+ getLightTransitionsController().setIconsDark(false /* dark */, true /* animate */);
+ } else {
+ mTintController.start();
+ }
+ }
+
private boolean shouldDeadZoneConsumeTouchEvents(MotionEvent event) {
if (mDeadZone.onTouchEvent(event) || mDeadZoneConsuming) {
switch (event.getActionMasked()) {
@@ -978,9 +990,9 @@
// Color adaption is tied with showing home handle, only avaliable if visible
if (visible) {
- mColorAdaptionController.start();
+ mTintController.start();
} else {
- mColorAdaptionController.stop();
+ mTintController.stop();
}
}
@@ -1206,6 +1218,19 @@
reorient();
notifyVerticalChangedListener(newVertical);
}
+
+ if (QuickStepContract.isGesturalMode(getContext())) {
+ // Update the nav bar background to match the height of the visible nav bar
+ int height = mIsVertical
+ ? getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height_landscape)
+ : getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ int frameHeight = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
+ }
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@@ -1232,9 +1257,9 @@
}
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- mColorAdaptionController.start();
+ mTintController.start();
} else {
- mColorAdaptionController.stop();
+ mTintController.stop();
}
}
@@ -1417,7 +1442,7 @@
mGestureHelper.dump(pw);
}
mRecentsOnboarding.dump(pw);
- mColorAdaptionController.dump(pw);
+ mTintController.dump(pw);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
index 81a425c..7dc71f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java
@@ -57,6 +57,7 @@
mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor);
mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor);
mPaint.setAntiAlias(true);
+ setFocusable(false);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
index 267468f..f03c234 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java
@@ -27,10 +27,13 @@
import android.widget.TextView;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -38,12 +41,15 @@
public final class BubbleClockControllerTest extends SysuiTestCase {
private BubbleClockController mClockController;
+ @Mock SysuiColorExtractor mMockColorExtractor;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
Resources res = getContext().getResources();
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
- mClockController = new BubbleClockController(res, layoutInflater);
+ mClockController = new BubbleClockController(res, layoutInflater, mMockColorExtractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
index 0659b4f..26fa62b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/StretchAnalogClockControllerTest.java
@@ -27,10 +27,13 @@
import android.widget.TextView;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -38,12 +41,16 @@
public final class StretchAnalogClockControllerTest extends SysuiTestCase {
private StretchAnalogClockController mClockController;
+ @Mock SysuiColorExtractor mMockColorExtractor;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
Resources res = getContext().getResources();
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
- mClockController = new StretchAnalogClockController(res, layoutInflater);
+ mClockController = new StretchAnalogClockController(res, layoutInflater,
+ mMockColorExtractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt
new file mode 100644
index 0000000..d9ef7fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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.keyguard.clock
+
+import android.content.Context
+import com.google.common.truth.Truth.assertThat
+
+import android.graphics.Canvas
+import android.graphics.Color
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ViewPreviewerTest : SysuiTestCase() {
+
+ private lateinit var previewer: ViewPreviewer
+ private lateinit var view: View
+
+ @Before
+ fun setUp() {
+ previewer = ViewPreviewer()
+ view = TestView(context)
+ }
+
+ @Test
+ fun testCreatePreview() {
+ val width = 100
+ val height = 100
+ // WHEN a preview image is created
+ val bitmap = previewer.createPreview(view, width, height)
+ // THEN the bitmap has the expected width and height
+ assertThat(bitmap.height).isEqualTo(height)
+ assertThat(bitmap.width).isEqualTo(width)
+ assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED)
+ }
+
+ class TestView(context: Context) : View(context) {
+ override fun onDraw(canvas: Canvas?) {
+ super.onDraw(canvas)
+ canvas?.drawColor(Color.RED)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index cde3398..392c677 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -237,6 +237,18 @@
}
@Test
+ public void screenOff_softBlanks() throws Exception {
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.transitionTo(DOZE_AOD, DOZE);
+ assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+
+ mScreen.transitionTo(DOZE, DOZE_AOD);
+ mSensor.sendSensorEvent(2);
+ assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ }
+
+ @Test
public void pausingAod_unblanksAfterSensor() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index fd31013..47933ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -194,7 +194,7 @@
VPN_PACKAGE),
mFooterText.getText());
assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
- assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
// Same situation, but with organization name set
when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -220,7 +220,7 @@
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
mFooterText.getText());
assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
- assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
// Same situation, but with organization name set
when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -243,7 +243,7 @@
TestableLooper.get(this).processAllMessages();
assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
- assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
mFooterText.getText());
}
@@ -294,7 +294,7 @@
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
- assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns),
mFooterText.getText());
}
@@ -306,7 +306,7 @@
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
- assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
assertEquals(mContext.getString(
R.string.quick_settings_disclosure_managed_profile_named_vpn,
VPN_PACKAGE_2),
@@ -320,7 +320,7 @@
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
- assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn,
VPN_PACKAGE),
mFooterText.getText());
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 72ce9c4..989470f 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -29,6 +29,7 @@
import android.text.Html;
import android.text.Html.ImageGetter;
import android.util.Log;
+import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
@@ -111,8 +112,16 @@
@Override
public Drawable getDrawable(String source) {
// Should only reach this when fetching the VPN icon for the warning string.
- Drawable icon = getDrawable(R.drawable.ic_vpn_dialog);
+ final Drawable icon = getDrawable(R.drawable.ic_vpn_dialog);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+
+ final TypedValue tv = new TypedValue();
+ if (getTheme().resolveAttribute(android.R.attr.textColorPrimary, tv, true)) {
+ icon.setTint(getColor(tv.resourceId));
+ } else {
+ Log.w(TAG, "Unable to resolve theme color");
+ }
+
return icon;
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index f52b94f..ce39a70 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7158,6 +7158,10 @@
// OPEN: Settings > System > Aware > Info dialog
DIALOG_AWARE_STATUS = 1701;
+
+ // Open: Settings > app > bubble settings > confirmation dialog
+ DIALOG_APP_BUBBLE_SETTINGS = 1702;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 1ce0c52..af77df6 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1752,6 +1752,9 @@
// Firmware generated an alert
TYPE_FIRMWARE_ALERT = 4;
+
+ // IP Manager lost reachability to network neighbors
+ TYPE_IP_REACHABILITY_LOST = 5;
}
// What event triggered WifiIsUnusableEvent.
@@ -2012,6 +2015,10 @@
// Whether the primary registered cell of current entry is same as that of previous entry
optional bool is_same_registered_cell = 33;
+
+ // The device mobility state
+ optional DeviceMobilityStatePnoScanStats.DeviceMobilityState
+ device_mobility_state = 34;
}
message WifiUsabilityStats {
@@ -2041,6 +2048,9 @@
// Firmware generated an alert
TYPE_FIRMWARE_ALERT = 4;
+
+ // IP Manager lost reachability to network neighbors
+ TYPE_IP_REACHABILITY_LOST = 5;
}
// The current wifi usability state
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2ded1e5..be58389 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -323,16 +323,24 @@
});
mPackageManager.addOnPermissionsChangeListener(
uid -> {
- synchronized (mLock) {
- onPermissionsChangedLocked();
- }
+ // listener invoked on ui thread, move to our thread to reduce risk of blocking
+ // ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onPermissionsChangedLocked();
+ }
+ });
});
mActivityManager.addOnUidImportanceListener(
(uid, importance) -> {
- synchronized (mLock) {
- onUidImportanceChangedLocked(uid, importance);
- }
+ // listener invoked on ui thread, move to our thread to reduce risk of blocking
+ // ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onUidImportanceChangedLocked(uid, importance);
+ }
+ });
},
FOREGROUND_IMPORTANCE_CUTOFF);
mContext.getContentResolver().registerContentObserver(
@@ -394,9 +402,13 @@
LocalServices.getService(PowerManagerInternal.class);
localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
state -> {
- synchronized (mLock) {
- onBatterySaverModeChangedLocked(state.locationMode);
- }
+ // listener invoked on ui thread, move to our thread to reduce risk of blocking
+ // ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onBatterySaverModeChangedLocked(state.locationMode);
+ }
+ });
});
new PackageMonitor() {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 1a842f7..b3a667a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3947,6 +3947,23 @@
case "ak.alizandro.smartaudiobookplayer": // b/129084042
case "com.campmobile.snow": // b/128803870
case "com.qnap.qfile": // b/126374406
+ case "com.google.android.apps.photos": // b/125506293
+ case "com.facebook.mlite": // b/126561155
+ case "com.ss.android.ugc.trill": // b/126610656
+ case "com.instagram.android": // b/127526615
+ case "com.facebook.orca": // b/128255453
+ case "org.videolan.vlc": // b/128391743
+ case "vStudio.Android.Camera360": // b/128882110
+ case "com.twitter.android": // b/128948908
+ case "com.tumblr": // b/129022664
+ case "com.sina.weibo": // b/129029018
+ case "com.kwai.video": // b/129037235
+ case "com.fotoable.photocollage": // b/129236353
+ case "com.xvideostudio.videoeditor": // b/129247146
+ case "app.buzz.share": // b/129304005
+ case "com.ss.android.article.topbuzzvideo.en": // b/129303979
+ case "com.linecorp.b612.android": // b/129318512
+ case "com.google.android.GoogleCamera": // b/128326994
return true;
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 73e0439..1c99316 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -79,6 +79,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.OptionalInt;
+import java.util.stream.Collectors;
/**
* Since phone process can be restarted, this class provides a centralized place
@@ -260,8 +261,7 @@
static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
| PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
- | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
- | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -822,7 +822,10 @@
}
}
if ((events & PhoneStateListener
- .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0) {
+ .LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE) != 0
+ && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(
+ r.context, r.callerPid, r.callerUid, r.callingPackage,
+ "listen_active_data_subid_change")) {
try {
r.callback.onActiveDataSubIdChanged(mActiveDataSubId);
} catch (RemoteException ex) {
@@ -1764,12 +1767,23 @@
log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId);
}
+ // Create a copy to prevent the IPC call while checking carrier privilege under the lock.
+ List<Record> copiedRecords;
synchronized (mRecords) {
- mActiveDataSubId = activeDataSubId;
+ copiedRecords = new ArrayList<>(mRecords);
+ }
+ mActiveDataSubId = activeDataSubId;
- for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)) {
+ // Filter the record that does not listen to this change or does not have the permission.
+ copiedRecords = copiedRecords.stream().filter(r -> r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE)
+ && TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(
+ mContext, r.callerPid, r.callerUid, r.callingPackage,
+ "notifyActiveDataSubIdChanged")).collect(Collectors.toCollection(ArrayList::new));
+
+ synchronized (mRecords) {
+ for (Record r : copiedRecords) {
+ if (mRecords.contains(r)) {
try {
r.callback.onActiveDataSubIdChanged(activeDataSubId);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 8ccb6e2..9325d25 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -307,7 +307,6 @@
}
cancelJobToUpdateAdbKeyStore();
- mAdbKeyStore = null;
mConnectedKey = null;
break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 1751856..b759dd4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -20,8 +20,10 @@
import android.app.ActivityThread;
import android.content.ContentResolver;
+import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertyChangedListener;
@@ -38,6 +40,7 @@
* Settings constants that can modify the activity manager's behavior.
*/
final class ActivityManagerConstants extends ContentObserver {
+ private static final String TAG = "ActivityManagerConstants";
// Key names stored in the settings value.
private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
@@ -272,6 +275,16 @@
// memory trimming.
public int CUR_TRIM_CACHED_PROCESSES;
+ private static final long MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES = 100 * 1024; // 100 KB
+
+ private final boolean mSystemServerAutomaticHeapDumpEnabled;
+
+ /** Package to report to when the memory usage exceeds the limit. */
+ private final String mSystemServerAutomaticHeapDumpPackageName;
+
+ /** Byte limit for dump heap monitoring. */
+ private long mSystemServerAutomaticHeapDumpPssThresholdBytes;
+
private static final Uri ACTIVITY_MANAGER_CONSTANTS_URI = Settings.Global.getUriFor(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS);
@@ -286,6 +299,9 @@
Settings.Global.getUriFor(
Settings.Global.BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST);
+ private static final Uri ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI =
+ Settings.Global.getUriFor(Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS);
+
private final OnPropertyChangedListener mOnDeviceConfigChangedListener =
new OnPropertyChangedListener() {
@Override
@@ -296,9 +312,17 @@
}
};
- public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
+ ActivityManagerConstants(Context context, ActivityManagerService service, Handler handler) {
super(handler);
mService = service;
+ mSystemServerAutomaticHeapDumpEnabled = Build.IS_DEBUGGABLE
+ && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_debugEnableAutomaticSystemServerHeapDumps);
+ mSystemServerAutomaticHeapDumpPackageName = context.getPackageName();
+ mSystemServerAutomaticHeapDumpPssThresholdBytes = Math.max(
+ MIN_AUTOMATIC_HEAP_DUMP_PSS_THRESHOLD_BYTES,
+ context.getResources().getInteger(
+ com.android.internal.R.integer.config_debugSystemServerPssThresholdBytes));
}
public void start(ContentResolver resolver) {
@@ -308,10 +332,17 @@
mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST_URI,
false, this);
+ if (mSystemServerAutomaticHeapDumpEnabled) {
+ mResolver.registerContentObserver(ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI,
+ false, this);
+ }
updateConstants();
updateActivityStartsLoggingEnabled();
updateBackgroundActivityStartsEnabled();
updateBackgroundActivityStartsPackageNamesWhitelist();
+ if (mSystemServerAutomaticHeapDumpEnabled) {
+ updateEnableAutomaticSystemServerHeapDumps();
+ }
DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ActivityThread.currentApplication().getMainExecutor(),
mOnDeviceConfigChangedListener);
@@ -343,6 +374,8 @@
updateBackgroundActivityStartsEnabled();
} else if (BACKGROUND_ACTIVITY_STARTS_PACKAGE_NAMES_WHITELIST_URI.equals(uri)) {
updateBackgroundActivityStartsPackageNamesWhitelist();
+ } else if (ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_URI.equals(uri)) {
+ updateEnableAutomaticSystemServerHeapDumps();
}
}
@@ -450,6 +483,24 @@
mPackageNamesWhitelistedForBgActivityStarts = newSet;
}
+ private void updateEnableAutomaticSystemServerHeapDumps() {
+ if (!mSystemServerAutomaticHeapDumpEnabled) {
+ Slog.wtf(TAG,
+ "updateEnableAutomaticSystemServerHeapDumps called when leak detection "
+ + "disabled");
+ return;
+ }
+ // Monitoring is on by default, so if the setting hasn't been set by the user,
+ // monitoring should be on.
+ final boolean enabled = Settings.Global.getInt(mResolver,
+ Settings.Global.ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS, 1) == 1;
+
+ // Setting the threshold to 0 stops the checking.
+ final long threshold = enabled ? mSystemServerAutomaticHeapDumpPssThresholdBytes : 0;
+ mService.setDumpHeapDebugLimit(null, 0, threshold,
+ mSystemServerAutomaticHeapDumpPackageName);
+ }
+
private void updateMaxCachedProcesses() {
String maxCachedProcessesFlag = DeviceConfig.getProperty(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_CACHED_PROCESSES);
@@ -460,7 +511,7 @@
: mOverrideMaxCachedProcesses;
} catch (NumberFormatException e) {
// Bad flag value from Phenotype, revert to default.
- Slog.e("ActivityManagerConstants",
+ Slog.e(TAG,
"Unable to parse flag for max_cached_processes: " + maxCachedProcessesFlag, e);
CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 05ec954..f51589a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2288,7 +2288,8 @@
mBatteryStatsService = null;
mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null;
mHandlerThread = handlerThread;
- mConstants = hasHandlerThread ? new ActivityManagerConstants(this, mHandler) : null;
+ mConstants = hasHandlerThread
+ ? new ActivityManagerConstants(mContext, this, mHandler) : null;
final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
mProcessList.init(this, activeUids);
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
@@ -2336,7 +2337,7 @@
mProcStartHandlerThread.start();
mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
- mConstants = new ActivityManagerConstants(this, mHandler);
+ mConstants = new ActivityManagerConstants(mContext, this, mHandler);
final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
mProcessList.init(this, activeUids);
mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
@@ -8835,7 +8836,6 @@
mAtmInternal.updateTopComponentForFactoryTest();
retrieveSettings();
- final int currentUserId = mUserController.getCurrentUserId();
mUgmInternal.onSystemReady();
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
@@ -8849,6 +8849,16 @@
}
if (goingCallback != null) goingCallback.run();
+ // Check the current user here as a user can be started inside goingCallback.run() from
+ // other system services.
+ final int currentUserId = mUserController.getCurrentUserId();
+ Slog.i(TAG, "Current user:" + currentUserId);
+ if (currentUserId != UserHandle.USER_SYSTEM && !mUserController.isSystemUserStarted()) {
+ // User other than system user has started. Make sure that system user is already
+ // started before switching user.
+ throw new RuntimeException("System user not started while current user is:"
+ + currentUserId);
+ }
traceLog.traceBegin("ActivityManagerStartApps");
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(currentUserId), currentUserId);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 07c9cca..cc4116e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -956,15 +956,26 @@
final int oldUserId = getCurrentUserId();
if (oldUserId == userId) {
final UserState state = getStartedUserState(userId);
- if (state != null && state.state == STATE_RUNNING_UNLOCKED) {
- // We'll skip all later code, so we must tell listener it's already unlocked.
- try {
- unlockListener.onFinished(userId, null);
- } catch (RemoteException ignore) {
- // Ignore.
+ if (state == null) {
+ Slog.wtf(TAG, "Current user has no UserState");
+ // continue starting.
+ } else {
+ if (userId == UserHandle.USER_SYSTEM && state.state == STATE_BOOTING) {
+ // system user start explicitly requested. should continue starting as it
+ // is not in running state.
+ } else {
+ if (state.state == STATE_RUNNING_UNLOCKED) {
+ // We'll skip all later code, so we must tell listener it's already
+ // unlocked.
+ try {
+ unlockListener.onFinished(userId, null);
+ } catch (RemoteException ignore) {
+ // Ignore.
+ }
+ }
+ return true;
}
}
- return true;
}
if (foreground) {
@@ -1743,6 +1754,24 @@
return state.state != UserState.STATE_STOPPING && state.state != UserState.STATE_SHUTDOWN;
}
+ /**
+ * Check if system user is already started. Unlike other user, system user is in STATE_BOOTING
+ * even if it is not explicitly started. So isUserRunning cannot give the right state
+ * to check if system user is started or not.
+ * @return true if system user is started.
+ */
+ boolean isSystemUserStarted() {
+ synchronized (mLock) {
+ UserState uss = mStartedUsers.get(UserHandle.USER_SYSTEM);
+ if (uss == null) {
+ return false;
+ }
+ return uss.state == UserState.STATE_RUNNING_LOCKED
+ || uss.state == UserState.STATE_RUNNING_UNLOCKING
+ || uss.state == UserState.STATE_RUNNING_UNLOCKED;
+ }
+ }
+
UserInfo getCurrentUser() {
if ((mInjector.checkCallingPermission(INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) && (
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index b8810c8f..2df8982 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -24,6 +24,7 @@
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.os.IHwBinder.DeathRecipient;
@@ -49,9 +50,17 @@
@GuardedBy("mLock")
private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>();
+ // Map from module ID to RadioModule created by mServiceListener.onRegistration().
@GuardedBy("mLock")
private final Map<Integer, RadioModule> mModules = new HashMap<>();
+ // Map from module ID to TunerSession created by openSession().
+ //
+ // Because this service currently implements a 1 AIDL to 1 HAL policy, mTunerSessions is used to
+ // enforce the "aggresive open" policy mandated for IBroadcastRadio.openSession(). In the
+ // future, this solution will be replaced with a multiple-AIDL to 1 HAL implementation.
+ private final Map<Integer, TunerSession> mTunerSessions = new HashMap<>();
+
private IServiceNotification.Stub mServiceListener = new IServiceNotification.Stub() {
@Override
public void onRegistration(String fqName, String serviceName, boolean preexisting) {
@@ -72,6 +81,7 @@
}
Slog.v(TAG, "loaded broadcast radio module " + moduleId + ": " + serviceName
+ " (HAL 2.0)");
+ closeTunerSessionLocked(moduleId);
mModules.put(moduleId, module);
if (newService) {
@@ -96,6 +106,7 @@
synchronized (mLock) {
int moduleId = (int) cookie;
mModules.remove(moduleId);
+ closeTunerSessionLocked(moduleId);
for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
if (entry.getValue() == moduleId) {
@@ -152,16 +163,20 @@
RadioModule module = null;
synchronized (mLock) {
module = mModules.get(moduleId);
- }
- if (module == null) {
- throw new IllegalArgumentException("Invalid module ID");
+ if (module == null) {
+ throw new IllegalArgumentException("Invalid module ID");
+ }
+ closeTunerSessionLocked(moduleId);
}
- TunerSession session = module.openSession(callback);
- if (legacyConfig != null) {
- session.setConfiguration(legacyConfig);
+ TunerSession tunerSession = module.openSession(callback);
+ synchronized (mLock) {
+ mTunerSessions.put(moduleId, tunerSession);
}
- return session;
+ if (legacyConfig != null) {
+ tunerSession.setConfiguration(legacyConfig);
+ }
+ return tunerSession;
}
public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@@ -183,4 +198,12 @@
}
return aggregator;
}
+
+ private void closeTunerSessionLocked(int moduleId) {
+ TunerSession tunerSession = mTunerSessions.remove(moduleId);
+ if (tunerSession != null) {
+ Slog.d(TAG, "Closing previous TunerSession");
+ tunerSession.close(RadioTuner.ERROR_HARDWARE_FAILURE);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 9833507..05ca144 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -17,6 +17,7 @@
package com.android.server.broadcastradio.hal2;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.hardware.broadcastradio.V2_0.ConfigFlag;
import android.hardware.broadcastradio.V2_0.ITunerSession;
@@ -58,8 +59,22 @@
@Override
public void close() {
+ close(null);
+ }
+
+ /**
+ * Closes the TunerSession. If error is non-null, the client's onError() callback is invoked
+ * first with the specified error, see {@link
+ * android.hardware.radio.RadioTuner.Callback#onError}.
+ *
+ * @param error Optional error to send to client before session is closed.
+ */
+ public void close(@Nullable Integer error) {
synchronized (mLock) {
if (mIsClosed) return;
+ if (error != null) {
+ TunerCallback.dispatch(() -> mCallback.mClientCb.onError(error));
+ }
mIsClosed = true;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9986809..b140c1b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -409,21 +409,25 @@
}
private int setWifiTethering(final boolean enable) {
- int rval = TETHER_ERROR_MASTER_ERROR;
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mPublicSync) {
- mWifiTetherRequested = enable;
final WifiManager mgr = getWifiManager();
+ if (mgr == null) {
+ mLog.e("setWifiTethering: failed to get WifiManager!");
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
(!enable && mgr.stopSoftAp())) {
- rval = TETHER_ERROR_NO_ERROR;
+ mWifiTetherRequested = enable;
+ return TETHER_ERROR_NO_ERROR;
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
- return rval;
+
+ return TETHER_ERROR_MASTER_ERROR;
}
private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
@@ -942,6 +946,11 @@
public int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+ if (usbManager == null) {
+ mLog.e("setUsbTethering: failed to get UsbManager!");
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
+
synchronized (mPublicSync) {
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
: UsbManager.FUNCTION_NONE);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index a2882de..9e2fd4e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -74,7 +74,15 @@
@GuardedBy("mLock")
private boolean mSystemAudioControlFeatureEnabled;
- private boolean mTvSystemAudioModeSupport;
+ /**
+ * Indicates if the TV that the current device is connected to supports System Audio Mode or not
+ *
+ * <p>If the current device has no information on this, keep mTvSystemAudioModeSupport null
+ *
+ * <p>The boolean will be reset to null every time when the current device goes to standby
+ * or loses its physical address.
+ */
+ private Boolean mTvSystemAudioModeSupport = null;
// Whether ARC is available or not. "true" means that ARC is established between TV and
// AVR as audio receiver.
@@ -321,7 +329,7 @@
@ServiceThreadOnly
protected void onStandby(boolean initiatedByCec, int standbyAction) {
assertRunOnServiceThread();
- mTvSystemAudioModeSupport = false;
+ mTvSystemAudioModeSupport = null;
// Record the last state of System Audio Control before going to standby
synchronized (mLock) {
mService.writeStringSystemProperty(
@@ -1029,12 +1037,11 @@
* <p>The result of the query may be cached until Audio device type is put in standby or loses
* its physical address.
*/
- // TODO(amyjojo): making mTvSystemAudioModeSupport null originally and fix the logic.
void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
- if (!mTvSystemAudioModeSupport) {
+ if (mTvSystemAudioModeSupport == null) {
addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
} else {
- callback.onResult(true);
+ callback.onResult(mTvSystemAudioModeSupport);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 4cc08d8..4ed24ec 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1291,13 +1291,11 @@
lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL,
stats.naturalImportance);
}
- // Log Assistant override if it was itself overridden by System. Since System can't be
- // overridden, it never needs logging.
- if (mImportanceExplanationCode == MetricsEvent.IMPORTANCE_EXPLANATION_SYSTEM
- && mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
- lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
+ }
+ // Log Assistant override if present, whether or not importance calculation is complete.
+ if (mAssistantImportance != IMPORTANCE_UNSPECIFIED) {
+ lm.addTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST,
mAssistantImportance);
- }
}
return lm;
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 37dd63a..13ff873 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -422,8 +422,6 @@
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
if (oi != null) {
mImpl.onOverlayPackageUpgrading(packageName, userId);
- } else {
- mImpl.onTargetPackageUpgrading(packageName, userId);
}
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 15ed063..a3d6380 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -22,7 +22,6 @@
import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
import static android.content.om.OverlayInfo.STATE_OVERLAY_UPGRADING;
-import static android.content.om.OverlayInfo.STATE_TARGET_UPGRADING;
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;
@@ -30,12 +29,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.om.OverlayInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.util.ArrayUtils;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
@@ -54,8 +56,14 @@
* @see OverlayManagerService
*/
final class OverlayManagerServiceImpl {
- // Flags to use in conjunction with updateState.
+
+ /**
+ * @deprecated Not used. See {@link android.content.om.OverlayInfo#STATE_TARGET_UPGRADING}.
+ */
+ @Deprecated
private static final int FLAG_TARGET_IS_UPGRADING = 1 << 0;
+
+ // Flags to use in conjunction with updateState.
private static final int FLAG_OVERLAY_IS_UPGRADING = 1 << 1;
private final PackageManagerHelper mPackageManager;
@@ -247,9 +255,7 @@
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
}
- if (updateAllOverlaysForTarget(packageName, userId, 0)) {
- mListener.onOverlaysChanged(packageName, userId);
- }
+ updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -257,16 +263,7 @@
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, 0);
- }
-
- void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) {
- if (DEBUG) {
- Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId="
- + userId);
- }
-
- updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING);
+ updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) {
@@ -274,7 +271,7 @@
Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId);
}
- updateAllOverlaysForTarget(packageName, userId, 0);
+ updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -282,22 +279,27 @@
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- if (updateAllOverlaysForTarget(packageName, userId, 0)) {
- mListener.onOverlaysChanged(packageName, userId);
- }
+ updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
/**
* Update the state of any overlays for this target.
- *
- * Returns true if the system should refresh the app's overlay paths (i.e.
- * if the settings were modified for this target, or there is at least one
- * enabled framework overlay).
*/
- private boolean updateAllOverlaysForTarget(@NonNull final String targetPackageName,
+ private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
final int userId, final int flags) {
+ final List<OverlayInfo> ois = new ArrayList<>();
+
+ // Framework overlays added first because order matters when resolving a resource
+ if (!"android".equals(targetPackageName)) {
+ ois.addAll(mSettings.getOverlaysForTarget("android", userId));
+ }
+
+ // Then add the targeted, non-framework overlays which have higher priority
+ ois.addAll(mSettings.getOverlaysForTarget(targetPackageName, userId));
+
+ final List<String> enabledBaseCodePaths = new ArrayList<>(ois.size());
+
boolean modified = false;
- final List<OverlayInfo> ois = mSettings.getOverlaysForTarget(targetPackageName, userId);
final int n = ois.size();
for (int i = 0; i < n; i++) {
final OverlayInfo oi = ois.get(i);
@@ -313,13 +315,35 @@
Slog.e(TAG, "failed to update settings", e);
modified |= mSettings.remove(oi.packageName, userId);
}
+
+ if (oi.isEnabled() && overlayPackage.applicationInfo != null) {
+ enabledBaseCodePaths.add(overlayPackage.applicationInfo.getBaseCodePath());
+ }
}
}
- // check for enabled framework overlays
- modified = modified || !getEnabledOverlayPackageNames("android", userId).isEmpty();
+ if (!modified) {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId);
+ ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo;
+ String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs;
- return modified;
+ // If the lists aren't the same length, the enabled overlays have changed
+ if (ArrayUtils.size(resourceDirs) != enabledBaseCodePaths.size()) {
+ modified = true;
+ } else if (resourceDirs != null) {
+ // If any element isn't equal, an overlay or the order of overlays has changed
+ for (int index = 0; index < resourceDirs.length; index++) {
+ if (!resourceDirs[index].equals(enabledBaseCodePaths.get(index))) {
+ modified = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (modified) {
+ mListener.onOverlaysChanged(targetPackageName, userId);
+ }
}
void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
@@ -670,10 +694,6 @@
@Nullable final PackageInfo overlayPackage, final int userId, final int flags)
throws OverlayManagerSettings.BadKeyException {
- if ((flags & FLAG_TARGET_IS_UPGRADING) != 0) {
- return STATE_TARGET_UPGRADING;
- }
-
if ((flags & FLAG_OVERLAY_IS_UPGRADING) != 0) {
return STATE_OVERLAY_UPGRADING;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index afa5ae9..ce3c452 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -396,6 +396,11 @@
} finally {
IoUtils.closeQuietly(fis);
}
+ // After all of the sessions were loaded, they are ready to be sealed and validated
+ for (int i = 0; i < mSessions.size(); ++i) {
+ PackageInstallerSession session = mSessions.valueAt(i);
+ session.sealAndValidateIfNecessary();
+ }
}
@GuardedBy("mSessions")
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f1d4524..5d539a4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -231,6 +231,8 @@
@GuardedBy("mLock")
private boolean mSealed = false;
@GuardedBy("mLock")
+ private boolean mShouldBeSealed = false;
+ @GuardedBy("mLock")
private boolean mCommitted = false;
@GuardedBy("mLock")
private boolean mRelinquished = false;
@@ -430,6 +432,7 @@
this.updatedMillis = createdMillis;
this.stageDir = stageDir;
this.stageCid = stageCid;
+ this.mShouldBeSealed = sealed;
if (childSessionIds != null) {
for (int childSessionId : childSessionIds) {
mChildSessionIds.put(childSessionId, 0);
@@ -450,16 +453,6 @@
mStagedSessionErrorCode = stagedSessionErrorCode;
mStagedSessionErrorMessage =
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
- if (sealed) {
- synchronized (mLock) {
- try {
- sealAndValidateLocked();
- } catch (PackageManagerException | IOException e) {
- destroyInternal();
- throw new IllegalArgumentException(e);
- }
- }
- }
}
public SessionInfo generateInfo() {
@@ -932,6 +925,8 @@
@NonNull IntentSender statusReceiver, boolean forTransfer) {
Preconditions.checkNotNull(statusReceiver);
+ List<PackageInstallerSession> childSessions = getChildSessions();
+
final boolean wasSealed;
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
@@ -963,7 +958,7 @@
wasSealed = mSealed;
if (!mSealed) {
try {
- sealAndValidateLocked();
+ sealAndValidateLocked(childSessions);
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (PackageManagerException e) {
@@ -994,21 +989,91 @@
return true;
}
+ /** Return a list of child sessions or null if the session is not multipackage
+ *
+ * <p> This method is handy to prevent potential deadlocks (b/123391593)
+ */
+ private @Nullable List<PackageInstallerSession> getChildSessions() {
+ List<PackageInstallerSession> childSessions = null;
+ if (isMultiPackage()) {
+ final int[] childSessionIds = getChildSessionIds();
+ childSessions = new ArrayList<>(childSessionIds.length);
+ for (int childSessionId : childSessionIds) {
+ childSessions.add(mSessionProvider.getSession(childSessionId));
+ }
+ }
+ return childSessions;
+ }
+
+ /**
+ * Assert multipackage install has consistent sessions.
+ *
+ * @throws PackageManagerException if child sessions don't match parent session
+ * in respect to staged and enable rollback parameters.
+ */
+ @GuardedBy("mLock")
+ private void assertMultiPackageConsistencyLocked(
+ @NonNull List<PackageInstallerSession> childSessions) throws PackageManagerException {
+ for (PackageInstallerSession childSession : childSessions) {
+ // It might be that the parent session is loaded before all of it's child sessions are,
+ // e.g. when reading sessions from XML. Those sessions will be null here, and their
+ // conformance with the multipackage params will be checked when they're loaded.
+ if (childSession == null) {
+ continue;
+ }
+ assertConsistencyWithLocked(childSession);
+ }
+ }
+
+ /**
+ * Assert consistency with the given session.
+ *
+ * @throws PackageManagerException if other sessions doesn't match this session
+ * in respect to staged and enable rollback parameters.
+ */
+ @GuardedBy("mLock")
+ private void assertConsistencyWithLocked(PackageInstallerSession other)
+ throws PackageManagerException {
+ // Session groups must be consistent wrt to isStaged parameter. Non-staging session
+ // cannot be grouped with staging sessions.
+ if (this.params.isStaged != other.params.isStaged) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
+ "Multipackage Inconsistency: session " + other.sessionId
+ + " and session " + sessionId
+ + " have inconsistent staged settings");
+ }
+ if (this.params.getEnableRollback() != other.params.getEnableRollback()) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_MULTIPACKAGE_INCONSISTENCY,
+ "Multipackage Inconsistency: session " + other.sessionId
+ + " and session " + sessionId
+ + " have inconsistent rollback settings");
+ }
+ }
+
/**
* Seal the session to prevent further modification and validate the contents of it.
*
* <p>The session will be sealed after calling this method even if it failed.
*
+ * @param childSessions the child sessions of a multipackage that will be checked for
+ * consistency. Can be null if session is not multipackage.
* @throws PackageManagerException if the session was sealed but something went wrong. If the
* session was sealed this is the only possible exception.
*/
@GuardedBy("mLock")
- private void sealAndValidateLocked() throws PackageManagerException, IOException {
+ private void sealAndValidateLocked(List<PackageInstallerSession> childSessions)
+ throws PackageManagerException, IOException {
assertNoWriteFileTransfersOpenLocked();
assertPreparedAndNotDestroyedLocked("sealing of session");
mSealed = true;
+ if (childSessions != null) {
+ assertMultiPackageConsistencyLocked(childSessions);
+ }
+
if (params.isStaged) {
final PackageInstallerSession activeSession = mStagingManager.getActiveSession();
final boolean anotherSessionAlreadyInProgress =
@@ -1048,6 +1113,38 @@
}
}
+ /**
+ * If session should be sealed, then it's sealed to prevent further modification
+ * and then it's validated.
+ *
+ * If the session was sealed but something went wrong then it's destroyed.
+ *
+ * <p> This is meant to be called after all of the sessions are loaded and added to
+ * PackageInstallerService
+ */
+ void sealAndValidateIfNecessary() {
+ synchronized (mLock) {
+ if (!mShouldBeSealed) {
+ return;
+ }
+ }
+ List<PackageInstallerSession> childSessions = getChildSessions();
+ synchronized (mLock) {
+ try {
+ sealAndValidateLocked(childSessions);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "Package not valid", e);
+ // Session is sealed but could not be verified, we need to destroy it.
+ destroyInternal();
+ // Dispatch message to remove session from PackageInstallerService
+ dispatchSessionFinished(
+ e.error, ExceptionUtils.getCompleteMessage(e), null);
+ }
+ }
+ }
+
/** Update the timestamp of when the staged session last changed state */
public void markUpdated() {
synchronized (mLock) {
@@ -1076,12 +1173,14 @@
throw new SecurityException("Can only transfer sessions that use public options");
}
+ List<PackageInstallerSession> childSessions = getChildSessions();
+
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("transfer");
try {
- sealAndValidateLocked();
+ sealAndValidateLocked(childSessions);
} catch (IOException e) {
throw new IllegalStateException(e);
} catch (PackageManagerException e) {
@@ -1132,14 +1231,7 @@
// outside of the lock, because reading the child
// sessions with the lock held could lead to deadlock
// (b/123391593).
- List<PackageInstallerSession> childSessions = null;
- if (isMultiPackage()) {
- final int[] childSessionIds = getChildSessionIds();
- childSessions = new ArrayList<>(childSessionIds.length);
- for (int childSessionId : childSessionIds) {
- childSessions.add(mSessionProvider.getSession(childSessionId));
- }
- }
+ List<PackageInstallerSession> childSessions = getChildSessions();
try {
synchronized (mLock) {
@@ -1965,15 +2057,6 @@
+ " does not exist"),
false, true).rethrowAsRuntimeException();
}
- // Session groups must be consistent wrt to isStaged parameter. Non-staging session
- // cannot be grouped with staging sessions.
- if (this.params.isStaged ^ childSession.params.isStaged) {
- throw new RemoteException("Unable to add child.",
- new PackageManagerException("Child session " + childSessionId
- + " and parent session " + this.sessionId + " do not have consistent"
- + " staging session settings."),
- false, true);
- }
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotSealedLocked("addChildSessionId");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3833afc..3cab9e5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20272,6 +20272,11 @@
return mContext.getString(R.string.config_defaultTextClassifierPackage);
}
+ @Override
+ public String getAttentionServicePackageName() {
+ return mContext.getString(R.string.config_defaultAttentionService);
+ }
+
private @Nullable String getDocumenterPackageName() {
final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 096335e..6a1f223 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4383,6 +4383,7 @@
ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION",
ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE",
ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, "ALLOW_AUDIO_PLAYBACK_CAPTURE",
+ ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, "ALLOW_EXTERNAL_STORAGE_SANDBOX",
ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e6e2c76..1833200 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -740,6 +740,14 @@
LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
}
+ // Atthention Service
+ String attentionServicePackageName =
+ mContext.getPackageManager().getAttentionServicePackageName();
+ if (!TextUtils.isEmpty(attentionServicePackageName)) {
+ grantPermissionsToSystemPackage(attentionServicePackageName, userId,
+ CAMERA_PERMISSIONS);
+ }
+
// There is no real "marker" interface to identify the shared storage backup, it is
// hardcoded in BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE.
grantSystemFixedPermissionsToSystemPackage("com.android.sharedstoragebackup", userId,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d4d752f..88109d3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2223,6 +2223,11 @@
@Override
public boolean canBeHiddenByKeyguardLw(WindowState win) {
+
+ // Keyguard visibility of window from activities are determined over activity visibility.
+ if (win.getAppToken() != null) {
+ return false;
+ }
switch (win.getAttrs().type) {
case TYPE_STATUS_BAR:
case TYPE_NAVIGATION_BAR:
@@ -2236,19 +2241,30 @@
}
private boolean shouldBeHiddenByKeyguard(WindowState win, WindowState imeTarget) {
+ final LayoutParams attrs = win.getAttrs();
- // Keyguard visibility of window from activities are determined over activity visibility.
- if (win.getAppToken() != null) {
- return false;
+ boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
+ && !mWindowManagerInternal.isStackVisibleLw(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ if (hideDockDivider) {
+ return true;
}
- final LayoutParams attrs = win.getAttrs();
+ // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered
+ // hidden because it's in the process of hiding, but it's still being shown on screen.
+ // In that case, we want to continue hiding the IME until the windows have completed
+ // drawing. This way, we know that the IME can be safely shown since the other windows are
+ // now shown.
+ final boolean hideIme = win.isInputMethodWindow()
+ && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
+ if (hideIme) {
+ return true;
+ }
+
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleLw()
&& (imeTarget.canShowWhenLocked() || !canBeHiddenByKeyguardLw(imeTarget));
// Show IME over the keyguard if the target allows it
- boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this)
- && showImeOverKeyguard;
+ boolean allowWhenLocked = win.isInputMethodWindow() && showImeOverKeyguard;
final boolean isKeyguardShowing = mKeyguardDelegate.isShowing();
@@ -2259,17 +2275,7 @@
|| (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
}
- boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
- && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered
- // hidden because it's in the process of hiding, but it's still being shown on screen.
- // In that case, we want to continue hiding the IME until the windows have completed
- // drawing. This way, we know that the IME can be safely shown since the other windows are
- // now shown.
- final boolean hideIme = win.isInputMethodWindow()
- && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete());
- return (isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
- || hideDockDivider || hideIme;
+ return isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY;
}
/** {@inheritDoc} */
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index f23b68e..38bdc62 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -594,7 +594,7 @@
boolean forceBackgroundCheck,
int locationMode) {
- this.adjustBrightnessFactor = adjustBrightnessFactor;
+ this.adjustBrightnessFactor = Math.min(1, Math.max(0, adjustBrightnessFactor));
this.advertiseIsEnabled = advertiseIsEnabled;
this.deferFullBackup = deferFullBackup;
this.deferKeyValueBackup = deferKeyValueBackup;
@@ -613,7 +613,14 @@
this.filesForNoninteractive = filesForNoninteractive;
this.forceAllAppsStandby = forceAllAppsStandby;
this.forceBackgroundCheck = forceBackgroundCheck;
- this.locationMode = locationMode;
+
+ if (locationMode < PowerManager.MIN_LOCATION_MODE
+ || PowerManager.MAX_LOCATION_MODE < locationMode) {
+ Slog.e(TAG, "Invalid location mode: " + locationMode);
+ this.locationMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+ } else {
+ this.locationMode = locationMode;
+ }
mHashCode = Objects.hash(
adjustBrightnessFactor,
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 0c9f815..c2a4339 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1719,6 +1719,7 @@
final WallpaperData systemWallpaper =
getWallpaperSafeLocked(userId, FLAG_SYSTEM);
switchWallpaper(systemWallpaper, null);
+ notifyCallbacksLocked(systemWallpaper);
}
// Make sure that the SELinux labeling of all the relevant files is correct.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5bbabfc..a93bdba 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1102,7 +1102,7 @@
ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
* 1000000L, fullscreen,
(info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
- info.screenOrientation, mRotationAnimationHint, info.configChanges,
+ info.screenOrientation, mRotationAnimationHint,
mLaunchTaskBehind, isAlwaysFocusable());
if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
Slog.v(TAG, "addAppToken: "
@@ -1151,11 +1151,11 @@
AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
- int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+ int rotationAnimationHint, boolean launchTaskBehind,
boolean alwaysFocusable) {
return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc,
inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
- rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+ rotationAnimationHint, launchTaskBehind, alwaysFocusable,
this);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c1b9bba..a53f85d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -153,7 +153,6 @@
/** @see WindowContainer#fillsParent() */
private boolean mFillsParent;
- boolean layoutConfigChanges;
boolean mShowForAllUsers;
int mTargetSdk;
@@ -337,7 +336,7 @@
AppWindowToken(WindowManagerService service, IApplicationToken token,
ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
- int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
+ int targetSdk, int orientation, int rotationAnimationHint,
boolean launchTaskBehind, boolean alwaysFocusable,
ActivityRecord activityRecord) {
this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
@@ -348,7 +347,6 @@
mShowForAllUsers = showForAllUsers;
mTargetSdk = targetSdk;
mOrientation = orientation;
- layoutConfigChanges = (configChanges & (CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION)) != 0;
mLaunchTaskBehind = launchTaskBehind;
mAlwaysFocusable = alwaysFocusable;
mRotationAnimationHint = rotationAnimationHint;
@@ -1976,7 +1974,7 @@
final boolean surfaceReady = w.isDrawnLw() // Regular case
|| w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
- final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady;
+ final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
if (needsLetterbox) {
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null));
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 9bc8462..3bbe28d 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -173,8 +173,9 @@
}
final boolean wasVis = mWin.isVisibleLw();
final boolean wasAnim = mWin.isAnimatingLw();
- final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnimation())
- : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnimation());
+ final boolean skipAnim = skipAnimation();
+ final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnim)
+ : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnim);
mNoAnimationOnNextShow = false;
final int state = computeStateLw(wasVis, wasAnim, mWin, change);
final boolean stateChanged = updateStateLw(state);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0c34e25..77055c1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -28,6 +28,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
@@ -655,7 +656,7 @@
if (DEBUG_LAYOUT && !w.mLayoutAttached) {
Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame
+ " mLayoutAttached=" + w.mLayoutAttached
- + " screen changed=" + w.isConfigChanged());
+ + " config reported=" + w.isLastConfigReportedToClient());
final AppWindowToken atoken = w.mAppToken;
if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility
+ " mRelayoutCalled=" + w.mRelayoutCalled + " hidden=" + w.mToken.isHidden()
@@ -670,42 +671,34 @@
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
- if (!gone || !w.mHaveFrame || w.mLayoutNeeded
- || ((w.isConfigChanged() || w.setReportResizeHints())
- && !w.isGoneForLayoutLw() &&
- ((w.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
- (w.mHasSurface && w.mAppToken != null &&
- w.mAppToken.layoutConfigChanges)))) {
- if (!w.mLayoutAttached) {
- if (mTmpInitial) {
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
- w.resetContentChanged();
- }
- if (w.mAttrs.type == TYPE_DREAM) {
- // Don't layout windows behind a dream, so that if it does stuff like hide
- // the status bar we won't get a bad transition when it goes away.
- mTmpWindow = w;
- }
- w.mLayoutNeeded = false;
- w.prelayout();
- final boolean firstLayout = !w.isLaidOut();
- getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
- w.mLayoutSeq = mLayoutSeq;
-
- // If this is the first layout, we need to initialize the last inset values as
- // otherwise we'd immediately cause an unnecessary resize.
- if (firstLayout) {
- w.updateLastInsetValues();
- }
-
- if (w.mAppToken != null) {
- w.mAppToken.layoutLetterbox(w);
- }
-
- if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
- + " mContainingFrame=" + w.getContainingFrame()
- + " mDisplayFrame=" + w.getDisplayFrameLw());
+ if ((!gone || !w.mHaveFrame || w.mLayoutNeeded) && !w.mLayoutAttached) {
+ if (mTmpInitial) {
+ w.resetContentChanged();
}
+ if (w.mAttrs.type == TYPE_DREAM) {
+ // Don't layout windows behind a dream, so that if it does stuff like hide
+ // the status bar we won't get a bad transition when it goes away.
+ mTmpWindow = w;
+ }
+ w.mLayoutNeeded = false;
+ w.prelayout();
+ final boolean firstLayout = !w.isLaidOut();
+ getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
+ w.mLayoutSeq = mLayoutSeq;
+
+ // If this is the first layout, we need to initialize the last inset values as
+ // otherwise we'd immediately cause an unnecessary resize.
+ if (firstLayout) {
+ w.updateLastInsetValues();
+ }
+
+ if (w.mAppToken != null) {
+ w.mAppToken.layoutLetterbox(w);
+ }
+
+ if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+ + " mContainingFrame=" + w.getContainingFrame()
+ + " mDisplayFrame=" + w.getDisplayFrameLw());
}
};
@@ -3094,7 +3087,7 @@
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
assignChildLayers(getPendingTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
@@ -3105,7 +3098,7 @@
// prepareSurfaces. This allows us to synchronize Z-ordering changes with
// the hiding and showing of surfaces.
scheduleAnimation();
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
// TODO: This should probably be called any time a visual change is made to the hierarchy like
@@ -3659,9 +3652,14 @@
// FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think it is animating.
pendingLayoutChanges = 0;
- mDisplayPolicy.beginPostLayoutPolicyLw();
- forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
- pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
+ try {
+ mDisplayPolicy.beginPostLayoutPolicyLw();
+ forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
+ pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
"after finishPostLayoutPolicyLw", pendingLayoutChanges);
mInsetsStateController.onPostLayout();
@@ -3670,7 +3668,13 @@
mTmpApplySurfaceChangesTransactionState.reset();
mTmpRecoveringMemory = recoveringMemory;
- forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
+
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
+ try {
+ forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
prepareSurfaces();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
@@ -3720,11 +3724,6 @@
out.set(left, top, left + width, top + height);
}
- @Override
- public void getBounds(Rect out) {
- calculateBounds(mDisplayInfo, out);
- }
-
private void getBounds(Rect out, int orientation) {
getBounds(out);
@@ -3746,6 +3745,15 @@
}
void performLayout(boolean initial, boolean updateInputWindows) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
+ try {
+ performLayoutNoTrace(initial, updateInputWindows);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
}
@@ -3755,13 +3763,14 @@
final int dh = mDisplayInfo.logicalHeight;
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
- Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
+ Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw
+ + " dh=" + dh);
}
mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo,
calculateDisplayCutoutForRotation(mDisplayInfo.rotation));
- // TODO: Not sure if we really need to set the rotation here since we are updating from the
- // display info above...
+ // TODO: Not sure if we really need to set the rotation here since we are updating from
+ // the display info above...
mDisplayFrames.mRotation = mRotation;
mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
@@ -4801,20 +4810,25 @@
@Override
void prepareSurfaces() {
- final ScreenRotationAnimation screenRotationAnimation =
- mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
- if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
- screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
- mPendingTransaction.setMatrix(mWindowingLayer,
- mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
- mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
- mPendingTransaction.setPosition(mWindowingLayer,
- mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
- mPendingTransaction.setAlpha(mWindowingLayer,
- screenRotationAnimation.getEnterTransformation().getAlpha());
- }
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
+ try {
+ final ScreenRotationAnimation screenRotationAnimation =
+ mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+ if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+ screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
+ mPendingTransaction.setMatrix(mWindowingLayer,
+ mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+ mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+ mPendingTransaction.setPosition(mWindowingLayer,
+ mTmpFloats[Matrix.MTRANS_X], mTmpFloats[Matrix.MTRANS_Y]);
+ mPendingTransaction.setAlpha(mWindowingLayer,
+ screenRotationAnimation.getEnterTransformation().getAlpha());
+ }
- super.prepareSurfaces();
+ super.prepareSurfaces();
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
}
void assignStackOrdering() {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 95d8944..1888e94 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -103,13 +103,16 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.localLOGV;
+import android.Manifest.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.Px;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PixelFormat;
@@ -255,6 +258,9 @@
private int[] mNavigationBarHeightForRotationInCarMode = new int[4];
private int[] mNavigationBarWidthForRotationInCarMode = new int[4];
+ /** Cached value of {@link ScreenShapeHelper#getWindowOutsetBottomPx} */
+ @Px private int mWindowOutsetBottom;
+
private final StatusBarController mStatusBarController = new StatusBarController();
private final BarController mNavigationBarController = new BarController("NavigationBar",
@@ -731,6 +737,11 @@
return true;
}
+ private boolean hasStatusBarServicePermission(int pid, int uid) {
+ return mContext.checkPermission(permission.STATUS_BAR_SERVICE, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* Sanitize the layout parameters coming from a client. Allows the policy
* to do things like ensure that windows of a specific type can't take
@@ -740,7 +751,7 @@
* are modified in-place.
*/
public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
- boolean hasStatusBarServicePermission) {
+ int callingPid, int callingUid) {
final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
if (mScreenDecorWindows.contains(win)) {
@@ -748,7 +759,7 @@
// No longer has the flag set, so remove from the set.
mScreenDecorWindows.remove(win);
}
- } else if (isScreenDecor && hasStatusBarServicePermission) {
+ } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) {
mScreenDecorWindows.add(win);
}
@@ -1159,7 +1170,7 @@
final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
if (useOutsets) {
- int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+ int outset = mWindowOutsetBottom;
if (outset > 0) {
if (displayRotation == Surface.ROTATION_0) {
outOutsets.bottom += outset;
@@ -1479,12 +1490,13 @@
}
// apply any navigation bar insets
sTmpRect.setEmpty();
- mStatusBar.getWindowFrames().setFrames(displayFrames.mUnrestricted /* parentFrame */,
+ final WindowFrames windowFrames = mStatusBar.getWindowFrames();
+ windowFrames.setFrames(displayFrames.mUnrestricted /* parentFrame */,
displayFrames.mUnrestricted /* displayFrame */,
displayFrames.mStable /* overscanFrame */, displayFrames.mStable /* contentFrame */,
displayFrames.mStable /* visibleFrame */, sTmpRect /* decorFrame */,
displayFrames.mStable /* stableFrame */, displayFrames.mStable /* outsetFrame */);
- mStatusBar.getWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+ windowFrames.setDisplayCutout(displayFrames.mDisplayCutout);
// Let the status bar determine its size.
mStatusBar.computeFrameLw();
@@ -1524,8 +1536,9 @@
"dock=%s content=%s cur=%s", dockFrame.toString(),
displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
- if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
- && !mStatusBarController.wasRecentlyTranslucent()) {
+ if (!statusBarTranslucent && !mStatusBarController.wasRecentlyTranslucent()
+ && !mStatusBar.isAnimatingLw()) {
+
// If the opaque status bar is currently requested to be visible, and not in the
// process of animating on or off, then we can tell the app that it is covered by
// it.
@@ -2180,7 +2193,7 @@
final Rect osf = windowFrames.mOutsetFrame;
osf.set(cf.left, cf.top, cf.right, cf.bottom);
windowFrames.setHasOutsets(true);
- int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
+ int outset = mWindowOutsetBottom;
if (outset > 0) {
int rotation = displayFrames.mRotation;
if (rotation == Surface.ROTATION_0) {
@@ -2336,7 +2349,7 @@
}
// Voice interaction overrides both top fullscreen and top docked.
- if (affectsSystemUi && win.getAttrs().type == TYPE_VOICE_INTERACTION) {
+ if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION) {
if (mTopFullscreenOpaqueWindowState == null) {
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -2599,6 +2612,7 @@
// EXPERIMENT END
updateConfigurationAndScreenSizeDependentBehaviors();
+ mWindowOutsetBottom = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
}
void updateConfigurationAndScreenSizeDependentBehaviors() {
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index ab95e4b..8dae016 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -43,6 +44,9 @@
final SurfaceControl mInputSurface;
Rect mTmpClipRect = new Rect();
+ private final Rect mTmpRect = new Rect();
+ private final Point mOldPosition = new Point();
+ private final Rect mOldWindowCrop = new Rect();
InputConsumerImpl(WindowManagerService service, IBinder token, String name,
InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
@@ -112,16 +116,22 @@
}
void layout(SurfaceControl.Transaction t, int dw, int dh) {
- t.setPosition(mInputSurface, 0, 0);
-
- mTmpClipRect.set(0, 0, dw, dh);
- t.setWindowCrop(mInputSurface, mTmpClipRect);
+ mTmpRect.set(0, 0, dw, dh);
+ layout(t, mTmpRect);
}
void layout(SurfaceControl.Transaction t, Rect r) {
- t.setPosition(mInputSurface, r.left, r.top);
mTmpClipRect.set(0, 0, r.width(), r.height());
+
+ if (mOldPosition.equals(r.left, r.top) && mOldWindowCrop.equals(mTmpClipRect)) {
+ return;
+ }
+
+ t.setPosition(mInputSurface, r.left, r.top);
t.setWindowCrop(mInputSurface, mTmpClipRect);
+
+ mOldPosition.set(r.left, r.top);
+ mOldWindowCrop.set(mTmpClipRect);
}
void hide(SurfaceControl.Transaction t) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 5669451..835b9b1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -190,8 +191,13 @@
}
void layoutInputConsumers(int dw, int dh) {
- for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
- mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer");
+ for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+ mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -401,7 +407,7 @@
final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, null, mDisplayId);
private void updateInputWindows(boolean inDrag) {
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
@@ -429,7 +435,7 @@
mDisplayContent.scheduleAnimation();
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@Override
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ed5f665..1ca31f1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -65,6 +66,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.EventLog;
@@ -551,18 +553,26 @@
return leakedSurface || killedApps;
}
+ void performSurfacePlacement(boolean recoveringMemory) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
+ try {
+ performSurfacePlacementNoTrace(recoveringMemory);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
// "Something has changed! Let's make it correct now."
// TODO: Super crazy long method that should be broken down...
- void performSurfacePlacement(boolean recoveringMemory) {
+ void performSurfacePlacementNoTrace(boolean recoveringMemory) {
if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
+ Debug.getCallers(3));
int i;
- boolean updateInputWindowsNeeded = false;
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
- updateInputWindowsNeeded = mWmService.updateFocusedWindowLocked(
+ mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
@@ -586,6 +596,7 @@
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction(recoveringMemory);
@@ -593,6 +604,7 @@
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
@@ -621,10 +633,8 @@
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
- if (mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
- false /*updateInputWindows*/)) {
- updateInputWindowsNeeded = true;
- }
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+ false /*updateInputWindows*/);
}
if (isLayoutNeeded()) {
@@ -679,12 +689,6 @@
}
}
- // Finally update all input windows now that the window changes have stabilized.
- forAllDisplays(dc -> {
- dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
- dc.updateSystemGestureExclusion();
- });
-
mWmService.setHoldScreenLocked(mHoldScreen);
if (!mWmService.mDisplayFrozen) {
final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
@@ -710,7 +714,7 @@
if (mWmService.mWaitingForDrawnCallback != null
|| (mOrientationChangeComplete && !isLayoutNeeded()
- && !mUpdateRotation)) {
+ && !mUpdateRotation)) {
mWmService.checkDrawnWindowsLocked();
}
@@ -742,12 +746,11 @@
mChildren.get(displayNdx).checkCompleteDeferredRemoval();
}
- if (updateInputWindowsNeeded) {
- forAllDisplays(dc -> {
- dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
- });
- }
- forAllDisplays(DisplayContent::updateTouchExcludeRegion);
+ forAllDisplays(dc -> {
+ dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
+ dc.updateSystemGestureExclusion();
+ dc.updateTouchExcludeRegion();
+ });
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
@@ -776,7 +779,7 @@
}
}
- if (!curDisplay.isAppAnimating() && curDisplay.mAppTransition.isRunning()) {
+ if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppAnimating()) {
// We have finished the animation of an app transition. To do this, we have
// delayed a lot of operations like showing and hiding apps, moving apps in
// Z-order, etc.
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 20a874b..9fe4760 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -244,8 +244,6 @@
void calculateOutsets() {
if (mHasOutsets) {
InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
- } else {
- mOutsets.setEmpty();
}
}
@@ -373,7 +371,13 @@
* Sets whether the frame has outsets.
*/
public void setHasOutsets(boolean hasOutsets) {
+ if (mHasOutsets == hasOutsets) {
+ return;
+ }
mHasOutsets = hasOutsets;
+ if (!hasOutsets) {
+ mOutsets.setEmpty();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index d3f3711..9d80425 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -414,7 +414,7 @@
OnHardKeyboardStatusChangeListener listener);
/** Returns true if a stack in the windowing mode is currently visible. */
- public abstract boolean isStackVisible(int windowingMode);
+ public abstract boolean isStackVisibleLw(int windowingMode);
/**
* Requests the window manager to resend the windows for accessibility.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9e421c1..20d02ee 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1392,11 +1392,9 @@
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
- final boolean hasStatusBarServicePermission =
- mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
- == PackageManager.PERMISSION_GRANTED;
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
- displayPolicy.adjustWindowParamsLw(win, win.mAttrs, hasStatusBarServicePermission);
+ displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
+ Binder.getCallingUid());
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = displayPolicy.prepareAddWindowLw(win, attrs);
@@ -1932,6 +1930,11 @@
}
}
+ private boolean hasStatusBarPermission(int pid, int uid) {
+ return mContext.checkPermission(permission.STATUS_BAR, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
@@ -1940,13 +1943,8 @@
SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
int result = 0;
boolean configChanged;
- final boolean hasStatusBarPermission =
- mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
- == PackageManager.PERMISSION_GRANTED;
- final boolean hasStatusBarServicePermission =
- mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
- == PackageManager.PERMISSION_GRANTED;
-
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
long origId = Binder.clearCallingIdentity();
final int displayId;
synchronized (mGlobalLock) {
@@ -1973,13 +1971,13 @@
int attrChanges = 0;
int flagChanges = 0;
if (attrs != null) {
- displayPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
+ displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
// if they don't have the permission, mask out the status bar bits
if (seq == win.mSeq) {
int systemUiVisibility = attrs.systemUiVisibility
| attrs.subtreeSystemUiVisibility;
if ((systemUiVisibility & DISABLE_MASK) != 0) {
- if (!hasStatusBarPermission) {
+ if (!hasStatusBarPermission(pid, uid)) {
systemUiVisibility &= ~DISABLE_MASK;
}
}
@@ -2050,7 +2048,6 @@
&& viewVisibility == View.VISIBLE;
boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
|| becameVisible;
- final boolean isDefaultDisplay = win.isDefaultDisplay();
boolean focusMayChange = win.mViewVisibility != viewVisibility
|| ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
@@ -7215,11 +7212,9 @@
}
@Override
- public boolean isStackVisible(int windowingMode) {
- synchronized (mGlobalLock) {
- final DisplayContent dc = getDefaultDisplayContentLocked();
- return dc.isStackVisible(windowingMode);
- }
+ public boolean isStackVisibleLw(int windowingMode) {
+ final DisplayContent dc = getDefaultDisplayContentLocked();
+ return dc.isStackVisible(windowingMode);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 486b0da..11288d2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -308,6 +308,9 @@
*/
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
+ /** @see #isLastConfigReportedToClient() */
+ private boolean mLastConfigReportedToClient;
+
private final Configuration mTempConfiguration = new Configuration();
/**
@@ -1201,7 +1204,7 @@
}
boolean didFrameInsetsChange = setReportResizeHints();
- boolean configChanged = isConfigChanged();
+ boolean configChanged = !isLastConfigReportedToClient();
if (DEBUG_CONFIGURATION && configChanged) {
Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
}
@@ -1781,9 +1784,18 @@
return getDisplayContent().getBounds().equals(getBounds());
}
- /** Returns true if last applied config was not yet requested by client. */
- boolean isConfigChanged() {
- return !getLastReportedConfiguration().equals(getConfiguration());
+ /**
+ * @return {@code true} if last applied config was reported to the client already, {@code false}
+ * otherwise.
+ */
+ boolean isLastConfigReportedToClient() {
+ return mLastConfigReportedToClient;
+ }
+
+ @Override
+ void onMergedOverrideConfigurationChanged() {
+ super.onMergedOverrideConfigurationChanged();
+ mLastConfigReportedToClient = false;
}
void onWindowReplacementTimeout() {
@@ -2299,11 +2311,6 @@
void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
// We need to turn on screen regardless of visibility.
boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
- boolean allowTheaterMode =
- mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
- mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
- == 0;
- boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
// The screen will turn on if the following conditions are met
// 1. The window has the flag FLAG_TURN_SCREEN_ON
@@ -2317,6 +2324,11 @@
// be occurring while turning off the screen. This would lead to the screen incorrectly
// turning back on.
if (hasTurnScreenOnFlag) {
+ boolean allowTheaterMode = mWmService.mAllowTheaterModeWakeFromLayout
+ || Settings.Global.getInt(mWmService.mContext.getContentResolver(),
+ Settings.Global.THEATER_MODE_ON, 0) == 0;
+ boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
+
if (allowTheaterMode && canTurnScreenOn && !mPowerManagerWrapper.isInteractive()) {
if (DEBUG_VISIBILITY || DEBUG_POWER) {
Slog.v(TAG, "Relayout window turning screen on: " + this);
@@ -2365,6 +2377,7 @@
void setLastReportedMergedConfiguration(MergedConfiguration config) {
mLastReportedConfiguration.setTo(config);
+ mLastConfigReportedToClient = true;
}
void getLastReportedMergedConfiguration(MergedConfiguration config) {
@@ -2512,6 +2525,10 @@
}
boolean showLw(boolean doAnimation, boolean requestAnim) {
+ if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+ // Already showing.
+ return false;
+ }
if (isHiddenFromUserLocked()) {
return false;
}
@@ -2532,10 +2549,6 @@
// This is an alert window that is currently force hidden.
return false;
}
- if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
- // Already showing.
- return false;
- }
if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
@@ -3529,7 +3542,10 @@
}
void transformClipRectFromScreenToSurfaceSpace(Rect clipRect) {
- if (mHScale >= 0) {
+ if (mHScale == 1 && mVScale == 1) {
+ return;
+ }
+ if (mHScale >= 0) {
clipRect.left = (int) (clipRect.left / mHScale);
clipRect.right = (int) Math.ceil(clipRect.right / mHScale);
}
@@ -4385,7 +4401,7 @@
// scale function because we want to round things to make the crop
// always round to a larger rect to ensure we don't crop too
// much and hide part of the window that should be seen.
- if (inSizeCompatMode() && mInvGlobalScale != 1.0f) {
+ if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
final float scale = mInvGlobalScale;
systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
@@ -4440,7 +4456,12 @@
mWinAnimator.mEnteringAnimation = true;
- prepareWindowToDisplayDuringRelayout(wasVisible);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
+ try {
+ prepareWindowToDisplayDuringRelayout(wasVisible);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
if ((attrChanges & FORMAT_CHANGED) != 0) {
// If the format can't be changed in place, preserve the old surface until the app draws
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index acb9823..bef0f81 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -56,6 +56,7 @@
private float mSurfaceY = 0;
private int mSurfaceW = 0;
private int mSurfaceH = 0;
+ private Rect mSurfaceCrop = new Rect(0, 0, -1, -1);
// Initialize to the identity matrix.
private float mLastDsdx = 1;
@@ -171,26 +172,15 @@
}
}
- void disconnectInTransaction() {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- Slog.i(TAG, "Disconnecting client: " + this);
- }
-
- try {
- if (mSurfaceControl != null) {
- mSurfaceControl.disconnect();
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error disconnecting surface in: " + this, e);
- }
- }
-
void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
if (SHOW_TRANSACTIONS) logSurface(
"CROP " + clipRect.toShortString(), null);
try {
if (clipRect.width() > 0 && clipRect.height() > 0) {
- mSurfaceControl.setWindowCrop(clipRect);
+ if (!clipRect.equals(mSurfaceCrop)) {
+ mSurfaceControl.setWindowCrop(clipRect);
+ mSurfaceCrop.set(clipRect);
+ }
mHiddenForCrop = false;
updateVisibility();
} else {
@@ -212,7 +202,11 @@
"CLEAR CROP", null);
try {
Rect clipRect = new Rect(0, 0, -1, -1);
+ if (mSurfaceCrop.equals(clipRect)) {
+ return;
+ }
mSurfaceControl.setWindowCrop(clipRect);
+ mSurfaceCrop.set(clipRect);
} catch (RuntimeException e) {
Slog.w(TAG, "Error setting clearing crop of " + this, e);
if (!recoveringMemory) {
@@ -221,12 +215,6 @@
}
}
- void setLayerStackInTransaction(int layerStack) {
- if (mSurfaceControl != null) {
- mSurfaceControl.setLayerStack(layerStack);
- }
- }
-
void setPositionInTransaction(float left, float top, boolean recoveringMemory) {
setPosition(null, left, top, recoveringMemory);
}
diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
index 98bad93..3be5d31 100644
--- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
+++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
@@ -81,11 +81,24 @@
* @hide
*/
public WmDisplayCutout calculateRelativeTo(Rect frame) {
+ if (mFrameSize == null) {
+ return this;
+ }
+ final int insetRight = mFrameSize.getWidth() - frame.right;
+ final int insetBottom = mFrameSize.getHeight() - frame.bottom;
+ if (frame.left == 0 && frame.top == 0 && insetRight == 0 && insetBottom == 0) {
+ return this;
+ }
+ if (frame.left >= mInner.getSafeInsetLeft()
+ && frame.top >= mInner.getSafeInsetTop()
+ && insetRight >= mInner.getSafeInsetRight()
+ && insetBottom >= mInner.getSafeInsetBottom()) {
+ return NO_CUTOUT;
+ }
if (mInner.isEmpty()) {
return this;
}
- return inset(frame.left, frame.top,
- mFrameSize.getWidth() - frame.right, mFrameSize.getHeight() - frame.bottom);
+ return inset(frame.left, frame.top, insetRight, insetBottom);
}
/**
diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk
index cc59b0c..bd4ebbd 100644
--- a/services/robotests/backup/Android.mk
+++ b/services/robotests/backup/Android.mk
@@ -26,7 +26,7 @@
LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- bmgrlib \
+ bmgr \
bu \
services.backup \
services.core \
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 04abeca1..f39c716 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -34,7 +34,9 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
@@ -64,6 +66,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
@@ -321,6 +324,14 @@
verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
}
+ @Test
+ public void testExplicitSystenUserStartInBackground() {
+ setUpUser(UserHandle.USER_SYSTEM, 0);
+ assertFalse(mUserController.isSystemUserStarted());
+ assertTrue(mUserController.startUser(UserHandle.USER_SYSTEM, false, null));
+ assertTrue(mUserController.isSystemUserStarted());
+ }
+
private void setUpUser(int userId, int flags) {
UserInfo userInfo = new UserInfo(userId, "User" + userId, flags);
when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo);
@@ -417,6 +428,12 @@
@Override
void reportCurWakefulnessUsageEvent() {
}
+
+ @Override
+ boolean isRuntimeRestarted() {
+ // to pass all metrics related calls
+ return true;
+ }
}
private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 4b3d9cf..0792414 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -1148,6 +1148,11 @@
return null;
}
+ @Override
+ public String getAttentionServicePackageName() throws RemoteException {
+ return null;
+ }
+
public String getIncidentReportApproverPackageName() throws RemoteException {
return null;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index ee09c7e..7c22350 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -477,8 +477,9 @@
assertEquals(MetricsEvent.IMPORTANCE_EXPLANATION_APP,
logMaker.getTaggedData(
MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_INITIAL_EXPLANATION));
- // This field is only populated if the assistant was itself overridden by the system.
- assertNull(logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST));
+ // This field is populated whenever mImportanceExplanationCode is.
+ assertEquals(IMPORTANCE_LOW,
+ logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_IMPORTANCE_ASST));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 2ab48a9..5cef38d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -38,6 +38,7 @@
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.RectF;
+import android.os.Binder;
import android.os.IBinder;
import android.testing.TestableResources;
import android.util.Pair;
@@ -98,7 +99,8 @@
}
void addWindow(WindowState win) {
- mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */);
+ mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
+ Binder.getCallingUid());
assertEquals(WindowManagerGlobal.ADD_OKAY,
mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs));
win.mHasSurface = true;
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index a15f959..a1c5bbe 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -27,8 +27,8 @@
/**
* Defines available network information which includes corresponding subscription id,
- * network plmns and corresponding priority to be used for network selection by Alternative Network
- * Service.
+ * network plmns and corresponding priority to be used for network selection by Opportunistic
+ * Network Service when passed through {@link TelephonyManager#updateAvailableNetworks}
*/
public final class AvailableNetworkInfo implements Parcelable {
@@ -55,15 +55,19 @@
/**
* Priority for the subscription id.
- * Priorities are in the range of 1 to 3 where 1
- * has the highest priority.
+ * Priorities are in the range of {@link AvailableNetworkInfo#PRIORITY_LOW} to
+ * {@link AvailableNetworkInfo#PRIORITY_HIGH}
+ * Among all networks available after network scan, subId with highest priority is chosen
+ * for network selection. If there are more than one subId with highest priority then the
+ * network with highest RSRP is chosen.
*/
private int mPriority;
/**
* Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
- * If this entry is left empty, then the platform software will not scan the network
- * to revalidate the input else platform will scan and verify specified PLMNs are available.
+ * Opportunistic Network Service will scan and verify specified PLMNs are available.
+ * If this entry is left empty, then the Opportunistic Network Service will not scan the network
+ * to validate the network availability.
*/
private ArrayList<String> mMccMncs;
@@ -71,8 +75,8 @@
* Returns the frequency bands associated with the {@link #getMccMncs() MCC/MNCs}.
* Opportunistic network service will use these bands to scan.
*
- * When no specific bands are specified (empty array or null) CBRS band (B48) will be
- * used for network scan.
+ * When no specific bands are specified (empty array or null) CBRS band
+ * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
*
* See {@link AccessNetworkConstants} for details.
*/
@@ -89,8 +93,12 @@
}
/**
- * Return priority for the subscription id. Valid value will be within
- * [{@link AvailableNetworkInfo#PRIORITY_HIGH}, {@link AvailableNetworkInfo#PRIORITY_LOW}]
+ * Return priority for the subscription id.
+ * Priorities are in the range of {@link AvailableNetworkInfo#PRIORITY_LOW} to
+ * {@link AvailableNetworkInfo#PRIORITY_HIGH}
+ * Among all networks available after network scan, subId with highest priority is chosen
+ * for network selection. If there are more than one subId with highest priority then the
+ * network with highest RSRP is chosen.
* @return priority level
*/
public int getPriority() {
@@ -99,8 +107,9 @@
/**
* Return List of PLMN ids (MCC-MNC) associated with the sub ID.
- * If this entry is left empty, then the platform software will not scan the network
- * to revalidate the input.
+ * Opportunistic Network Service will scan and verify specified PLMNs are available.
+ * If this entry is left empty, then the Opportunistic Network Service will not scan the network
+ * to validate the network availability.
* @return list of PLMN ids
*/
public @NonNull List<String> getMccMncs() {
@@ -112,6 +121,9 @@
*
* The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
* {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}
+ * See {@link AccessNetworkConstants.AccessNetworkType} for details regarding different network
+ * types. When no specific bands are specified (empty array or null) CBRS band
+ * {@link AccessNetworkConstants.EutranBand#BAND_48} will be used for network scan.
*/
public @NonNull List<Integer> getBands() {
return (List<Integer>) mBands.clone();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6aca693..0b44367 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3123,14 +3123,14 @@
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
+ /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -118);
- /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_POOR */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -128);
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
+ /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
/* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 10);
- /* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_POOR */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, -30);
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
/* Default value is 1024 kbps */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024);
/* Default value is 10 seconds */
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 918bf60..373c5d2 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -297,8 +297,11 @@
* it could be the current active opportunistic subscription in use, or the
* subscription user selected as default data subscription in DSDS mode.
*
- * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
- * READ_PHONE_STATE}
+ * Requires Permission: No permission is required to listen, but notification requires
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or the calling
+ * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges})
+ * on any active subscription.
+ *
* @see #onActiveDataSubscriptionIdChanged
*/
public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 09046a6..63d427a 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -951,8 +951,7 @@
* @return associated subscription id
*/
public int getSubscriptionId() {
- final int subId = (mSubId == DEFAULT_SUBSCRIPTION_ID)
- ? getDefaultSmsSubscriptionId() : mSubId;
+ final int subId = getSubIdOrDefault();
boolean isSmsSimPickActivityNeeded = false;
final Context context = ActivityThread.currentApplication().getApplicationContext();
try {
@@ -985,6 +984,17 @@
}
/**
+ * @return the subscription ID associated with this {@link SmsManager} or the default set by the
+ * user if this instance was created using {@link SmsManager#getDefault}.
+ *
+ * If there is no default set by the user, this method returns
+ * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ */
+ private int getSubIdOrDefault() {
+ return (mSubId == DEFAULT_SUBSCRIPTION_ID) ? getDefaultSmsSubscriptionId() : mSubId;
+ }
+
+ /**
* Returns the ISms service, or throws an UnsupportedOperationException if
* the service does not exist.
*/
@@ -1151,8 +1161,9 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- success = iSms.enableCellBroadcastForSubscriber(
- getSubscriptionId(), messageIdentifier, ranType);
+ // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+ success = iSms.enableCellBroadcastForSubscriber(getSubIdOrDefault(),
+ messageIdentifier, ranType);
}
} catch (RemoteException ex) {
// ignore it
@@ -1187,8 +1198,9 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- success = iSms.disableCellBroadcastForSubscriber(
- getSubscriptionId(), messageIdentifier, ranType);
+ // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+ success = iSms.disableCellBroadcastForSubscriber(getSubIdOrDefault(),
+ messageIdentifier, ranType);
}
} catch (RemoteException ex) {
// ignore it
@@ -1230,7 +1242,8 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(),
+ // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+ success = iSms.enableCellBroadcastRangeForSubscriber(getSubIdOrDefault(),
startMessageId, endMessageId, ranType);
}
} catch (RemoteException ex) {
@@ -1273,7 +1286,8 @@
try {
ISms iSms = getISmsService();
if (iSms != null) {
- success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(),
+ // If getSubIdOrDefault() returns INVALID, we will use the default phone internally.
+ success = iSms.disableCellBroadcastRangeForSubscriber(getSubIdOrDefault(),
startMessageId, endMessageId, ranType);
}
} catch (RemoteException ex) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e4debd6..6dd1691 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10547,6 +10547,9 @@
/**
* Set preferred opportunistic data subscription id.
*
+ * Switch internet data to preferred opportunistic data subscription id. This api
+ * can result in lose of internet connectivity for short period of time while internet data
+ * is handed over.
* <p>Requires that the calling app has carrier privileges on both primary and
* secondary subscriptions (see
* {@link #hasCarrierPrivileges}), or has permission
@@ -10625,9 +10628,11 @@
*
* This api should be called to inform OpportunisticNetwork Service about the availability
* of a network at the current location. This information will be used by OpportunisticNetwork
- * service to decide to attach to the network opportunistically. If an empty list is passed,
+ * service to enable modem stack and to attach to the network. If an empty list is passed,
* it is assumed that no network is available and will result in disabling the modem stack
- * to save power.
+ * to save power. This api do not switch internet data once network attach is completed.
+ * Use {@link TelephonyManager#setPreferredOpportunisticDataSubscription}
+ * to switch internet data after network attach is complete.
* Requires that the calling app has carrier privileges on both primary and
* secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 8cdf6a2..cc037e3 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.content.Context;
@@ -38,25 +39,36 @@
import com.android.internal.telephony.ITelephony;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
* Manages IMS provisioning and configuration parameters, as well as callbacks for apps to listen
* to changes in these configurations.
*
- * Note: IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
- * applications and may vary. For compatibility purposes, the first 100 integer values used in
- * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
- * previously defined in the Android framework. Some common constants have been defined in this
- * class to make integrating with other system apps easier. USE WITH CARE!
+ * IMS provisioning keys are defined per carrier or OEM using OMA-DM or other provisioning
+ * applications and may vary. It is up to the carrier and OEM applications to ensure that the
+ * correct provisioning keys are being used when integrating with a vendor's ImsService.
*
- * To avoid collisions, please use String based configurations when possible:
- * {@link #setProvisioningStringValue(int, String)} and {@link #getProvisioningStringValue(int)}.
+ * Note: For compatibility purposes, the integer values [0 - 99] used in
+ * {@link #setProvisioningIntValue(int, int)} have been reserved for existing provisioning keys
+ * previously defined in the Android framework. Please do not redefine new provisioning keys in this
+ * range or it may generate collisions with existing keys. Some common constants have also been
+ * defined in this class to make integrating with other system apps easier.
* @hide
*/
@SystemApi
public class ProvisioningManager {
+ /**@hide*/
+ @StringDef(prefix = "STRING_QUERY_RESULT_ERROR_", value = {
+ STRING_QUERY_RESULT_ERROR_GENERIC,
+ STRING_QUERY_RESULT_ERROR_NOT_READY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StringResultError {}
+
/**
* The query from {@link #getProvisioningStringValue(int)} has resulted in an unspecified error.
*/
@@ -268,14 +280,13 @@
* This operation is blocking and should not be performed on the UI thread.
*
* @param key A String that represents the provisioning key, which is defined by the OEM.
- * @return a String value for the provided key, {@code null} if the key doesn't exist, or one
- * of the following error codes: {@link #STRING_QUERY_RESULT_ERROR_GENERIC},
- * {@link #STRING_QUERY_RESULT_ERROR_NOT_READY}.
+ * @return a String value for the provided key, {@code null} if the key doesn't exist, or
+ * {@link StringResultError} if there was an error getting the value for the provided key.
* @throws IllegalArgumentException if the key provided was invalid.
*/
@WorkerThread
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public @Nullable String getProvisioningStringValue(int key) {
+ public @Nullable @StringResultError String getProvisioningStringValue(int key) {
try {
return getITelephony().getImsProvisioningString(mSubId, key);
} catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index d93e582..f5985b4 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -164,6 +164,63 @@
// We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
// revoked.
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * Check whether the app with the given pid/uid can read phone state, or has carrier
+ * privileges on any active subscription.
+ *
+ * <p>If the app does not have carrier privilege, this method will return {@code false} instead
+ * of throwing a SecurityException. Therefore, the callers cannot tell the difference
+ * between M+ apps which declare the runtime permission but do not have it, and pre-M apps
+ * which declare the static permission but had access revoked via AppOps. Apps in the former
+ * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for
+ * use only if the behavior in both scenarios is meant to be identical.
+ *
+ * @return {@code true} if the app can read phone state or has carrier privilege;
+ * {@code false} otherwise.
+ */
+ public static boolean checkReadPhoneStateOnAnyActiveSub(
+ Context context, int pid, int uid, String callingPackage, String message) {
+ return checkReadPhoneStateOnAnyActiveSub(context, TELEPHONY_SUPPLIER, pid, uid,
+ callingPackage, message);
+ }
+
+ @VisibleForTesting
+ public static boolean checkReadPhoneStateOnAnyActiveSub(
+ Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid,
+ String callingPackage, String message) {
+ try {
+ context.enforcePermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
+
+ // SKIP checking for run-time permission since caller has PRIVILEGED permission
+ return true;
+ } catch (SecurityException privilegedPhoneStateException) {
+ try {
+ context.enforcePermission(
+ android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+ } catch (SecurityException phoneStateException) {
+ SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int[] activeSubIds = sm.getActiveSubscriptionIdList();
+ for (int activeSubId : activeSubIds) {
+ // If we don't have the runtime permission, but do have carrier privileges, that
+ // suffices for reading phone state.
+ if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
+ // revoked.
+ AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage) ==
AppOpsManager.MODE_ALLOWED;
}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index e17fb47..92f1ddb 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -2314,7 +2314,7 @@
int DumpManifest(LoadedApk* apk, DumpManifestOptions& options, text::Printer* printer,
IDiagnostics* diag) {
ManifestExtractor extractor(apk, options);
- return extractor.Dump(printer, diag);
+ return extractor.Dump(printer, diag) ? 0 : 1;
}
} // namespace aapt
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 78967e4..be227e7 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2190,22 +2190,7 @@
key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
}
} else {
- if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
- key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
- } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
- allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
- key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
- } else if (wepKeys[0] != null) {
- key = SSID + "WEP";
- } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
- key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
- } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
- key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
- } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
- key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
- } else {
- key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
- }
+ key = getSsidAndSecurityTypeString();
if (!shared) {
key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
}
@@ -2215,6 +2200,30 @@
}
/** @hide
+ * return the SSID + security type in String format.
+ */
+ public String getSsidAndSecurityTypeString() {
+ String key;
+ if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+ } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
+ || allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+ key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+ } else if (wepKeys[0] != null) {
+ key = SSID + "WEP";
+ } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
+ key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
+ } else if (allowedKeyManagement.get(KeyMgmt.SAE)) {
+ key = SSID + KeyMgmt.strings[KeyMgmt.SAE];
+ } else if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
+ key = SSID + KeyMgmt.strings[KeyMgmt.SUITE_B_192];
+ } else {
+ key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
+ }
+ return key;
+ }
+
+ /** @hide
* get configKey, force calculating the config string
*/
public String configKey() {
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index d927052..ba9fc78 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -348,4 +348,47 @@
}
assertTrue(exceptionThrown);
}
+
+ /**
+ * Verifies that getSsidAndSecurityTypeString returns the correct String for networks of
+ * various different security types
+ */
+ @Test
+ public void testGetSsidAndSecurityTypeString() {
+ WifiConfiguration config = new WifiConfiguration();
+ final String mSsid = "TestAP";
+ config.SSID = mSsid;
+
+ // Test various combinations
+ config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WPA_PSK],
+ config.getSsidAndSecurityTypeString());
+
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.WPA_EAP],
+ config.getSsidAndSecurityTypeString());
+
+ config.wepKeys[0] = "TestWep";
+ config.allowedKeyManagement.clear();
+ assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString());
+
+ config.wepKeys[0] = null;
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(KeyMgmt.OWE);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString());
+
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(KeyMgmt.SAE);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.SAE], config.getSsidAndSecurityTypeString());
+
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(KeyMgmt.SUITE_B_192);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.SUITE_B_192],
+ config.getSsidAndSecurityTypeString());
+
+ config.allowedKeyManagement.clear();
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.NONE], config.getSsidAndSecurityTypeString());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 600abc9..fa17db1 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1382,4 +1382,60 @@
r.run();
}
}
+
+ /**
+ * Test behavior of isEnhancedOpenSupported
+ * @throws Exception
+ */
+ @Test
+ public void testIsEnhancedOpenSupported() throws Exception {
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(WifiManager.WIFI_FEATURE_OWE));
+ assertTrue(mWifiManager.isEnhancedOpenSupported());
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(~WifiManager.WIFI_FEATURE_OWE));
+ assertFalse(mWifiManager.isEnhancedOpenSupported());
+ }
+
+ /**
+ * Test behavior of isWpa3SaeSupported
+ * @throws Exception
+ */
+ @Test
+ public void testIsWpa3SaeSupported() throws Exception {
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SAE));
+ assertTrue(mWifiManager.isWpa3SaeSupported());
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SAE));
+ assertFalse(mWifiManager.isWpa3SaeSupported());
+ }
+
+ /**
+ * Test behavior of isWpa3SuiteBSupported
+ * @throws Exception
+ */
+ @Test
+ public void testIsWpa3SuiteBSupported() throws Exception {
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+ assertTrue(mWifiManager.isWpa3SuiteBSupported());
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+ assertFalse(mWifiManager.isWpa3SuiteBSupported());
+ }
+
+ /**
+ * Test behavior of isEasyConnectSupported
+ * @throws Exception
+ */
+ @Test
+ public void testIsEasyConnectSupported() throws Exception {
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(WifiManager.WIFI_FEATURE_DPP));
+ assertTrue(mWifiManager.isEasyConnectSupported());
+ when(mWifiService.getSupportedFeatures())
+ .thenReturn(new Long(~WifiManager.WIFI_FEATURE_DPP));
+ assertFalse(mWifiManager.isEasyConnectSupported());
+ }
}