Merge "Settings entry for private space auto lock option" into main
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 807a006..47065e1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -108,7 +108,6 @@
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -3586,10 +3585,12 @@
|| applicationInfo.isSignedWithPlatformKey();
}
- public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
- List<String> names) {
+ private ArrayMap<String, String> getStringsForPrefixStripPrefix(
+ ContentResolver cr, String prefix, String[] names) {
String namespace = prefix.substring(0, prefix.length() - 1);
ArrayMap<String, String> keyValues = new ArrayMap<>();
+ int substringLength = prefix.length();
+
int currentGeneration = -1;
boolean needsGenerationTracker = false;
@@ -3619,10 +3620,13 @@
if (DEBUG) {
Log.i(TAG, "Cache hit for prefix:" + prefix);
}
- if (!names.isEmpty()) {
+ if (names.length > 0) {
for (String name : names) {
+ // mValues can contain "null" values, need to use containsKey.
if (mValues.containsKey(name)) {
- keyValues.put(name, mValues.get(name));
+ keyValues.put(
+ name.substring(substringLength),
+ mValues.get(name));
}
}
} else {
@@ -3631,7 +3635,10 @@
// Explicitly exclude the prefix as it is only there to
// signal that the prefix has been cached.
if (key.startsWith(prefix) && !key.equals(prefix)) {
- keyValues.put(key, mValues.get(key));
+ String value = mValues.valueAt(i);
+ keyValues.put(
+ key.substring(substringLength),
+ value);
}
}
}
@@ -3691,14 +3698,22 @@
Map<String, String> flagsToValues =
(HashMap) b.getSerializable(Settings.NameValueTable.VALUE, java.util.HashMap.class);
// Only the flags requested by the caller
- if (!names.isEmpty()) {
- for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
- if (names.contains(flag.getKey())) {
- keyValues.put(flag.getKey(), flag.getValue());
+ if (names.length > 0) {
+ for (String name : names) {
+ // flagsToValues can contain "null" values, need to use containsKey.
+ if (flagsToValues.containsKey(name)) {
+ keyValues.put(
+ name.substring(substringLength),
+ flagsToValues.get(name));
}
}
} else {
- keyValues.putAll(flagsToValues);
+ keyValues.ensureCapacity(keyValues.size() + flagsToValues.size());
+ for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
+ keyValues.put(
+ flag.getKey().substring(substringLength),
+ flag.getValue());
+ }
}
synchronized (NameValueCache.this) {
@@ -19884,21 +19899,15 @@
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public static Map<String, String> getStrings(@NonNull ContentResolver resolver,
@NonNull String namespace, @NonNull List<String> names) {
- List<String> compositeNames = new ArrayList<>(names.size());
- for (String name : names) {
- compositeNames.add(createCompositeName(namespace, name));
+ String[] compositeNames = new String[names.size()];
+ for (int i = 0, size = names.size(); i < size; ++i) {
+ compositeNames[i] = createCompositeName(namespace, names.get(i));
}
String prefix = createPrefix(namespace);
- ArrayMap<String, String> rawKeyValues = sNameValueCache.getStringsForPrefix(
+
+ ArrayMap<String, String> keyValues = sNameValueCache.getStringsForPrefixStripPrefix(
resolver, prefix, compositeNames);
- int size = rawKeyValues.size();
- int substringLength = prefix.length();
- ArrayMap<String, String> keyValues = new ArrayMap<>(size);
- for (int i = 0; i < size; ++i) {
- keyValues.put(rawKeyValues.keyAt(i).substring(substringLength),
- rawKeyValues.valueAt(i));
- }
return keyValues;
}
@@ -20224,12 +20233,13 @@
private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
Preconditions.checkNotNull(namespace);
Preconditions.checkNotNull(name);
- return createPrefix(namespace) + name;
+ var sb = new StringBuilder(namespace.length() + 1 + name.length());
+ return sb.append(namespace).append('/').append(name).toString();
}
private static String createPrefix(@NonNull String namespace) {
Preconditions.checkNotNull(namespace);
- return namespace + "/";
+ return namespace + '/';
}
private static Uri createNamespaceUri(@NonNull String namespace) {
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index ffe3d1d..8b16d64 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -62,6 +62,7 @@
],
lint: {
baseline_filename: "lint-baseline.xml",
+ extra_check_modules: ["SettingsLibLintChecker"],
},
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 3d1efa5..5827671 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -293,8 +293,8 @@
}
/**
- * Verifies that swiping up when the lock pattern is not secure does not consume the scroll
- * gesture or expand.
+ * Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes
+ * the gesture.
*/
@Test
public void testSwipeUp_keyguardNotSecure_doesNotExpand() {
@@ -314,11 +314,44 @@
reset(mScrimController);
- // Scroll gesture is not consumed.
+ // Scroll gesture is consumed.
assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
- .isFalse();
+ .isTrue();
// We should not expand since the keyguard is not secure
verify(mScrimController, never()).expand(any());
+ // Since we are swiping up, we should wake from dreams.
+ verify(mCentralSurfaces).awakenDreams();
+ }
+
+ /**
+ * Verifies that swiping down when the lock pattern is not secure does not dismiss the dream.
+ */
+ @Test
+ public void testSwipeDown_keyguardNotSecure_doesNotExpand() {
+ when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false);
+ mTouchHandler.onSessionStart(mTouchSession);
+ ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+ verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
+
+ final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
+
+ final float distanceY = SCREEN_HEIGHT_PX * 0.3f;
+ // Swiping down near the bottom of the screen where the touch initiation region is.
+ final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX - distanceY, 0);
+ final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
+ 0, SCREEN_HEIGHT_PX, 0);
+
+ reset(mScrimController);
+
+ // Scroll gesture is not consumed.
+ assertThat(gestureListener.onScroll(event1, event2, 0, distanceY))
+ .isTrue();
+ // We should not expand since the keyguard is not secure
+ verify(mScrimController, never()).expand(any());
+ // Since we are swiping down, we should not dismiss the dream.
+ verify(mCentralSurfaces, never()).awakenDreams();
}
private void verifyScroll(float percent, Direction direction,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 4e4b79c..7f3b5eb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -155,12 +155,12 @@
return true;
}
- // Don't set expansion if the user doesn't have a pin/password set so that no
- // animations are played we're not transitioning to the bouncer.
- if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
- // Return false so the gesture is not consumed, allowing the dream to wake
- // if it wants instead of doing nothing.
- return false;
+ // If scrolling up and keyguard is not locked, dismiss the dream since there's
+ // no bouncer to show.
+ if (e1.getY() > e2.getY()
+ && !mLockPatternUtils.isSecure(mUserTracker.getUserId())) {
+ mCentralSurfaces.get().awakenDreams();
+ return true;
}
// For consistency, we adopt the expansion definition found in the
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index dada72e..f80228a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1812,6 +1812,7 @@
"msg: MSG_L_SET_BT_ACTIVE_DEVICE "
+ "received with null profile proxy: "
+ btInfo)).printLog(TAG));
+ sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, 0 /*delay*/);
return;
}
@AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index dd9541e..92be4ee 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -698,7 +698,7 @@
pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId);
// Clear any existing archive state.
- pkgSetting.setArchiveState(null, userId);
+ mPm.mInstallerService.mPackageArchiver.clearArchiveState(packageName, userId);
mPm.mSettings.writePackageRestrictionsLPr(userId);
mPm.mSettings.writeKernelMappingLPr(pkgSetting);
installed = true;
@@ -2281,7 +2281,7 @@
installerPackageName);
}
// Clear any existing archive state.
- ps.setArchiveState(null, userId);
+ mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName, userId);
} else if (allUsers != null) {
// The caller explicitly specified INSTALL_ALL_USERS flag.
// Thus, updating the settings to install the app for all users.
@@ -2305,7 +2305,8 @@
installerPackageName);
}
// Clear any existing archive state.
- ps.setArchiveState(null, currentUserId);
+ mPm.mInstallerService.mPackageArchiver.clearArchiveState(pkgName,
+ currentUserId);
} else {
ps.setInstalled(false, currentUserId);
}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index a4af5e7..3e5759a 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -71,6 +71,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.IBinder;
import android.os.ParcelableException;
import android.os.Process;
@@ -309,6 +310,26 @@
return false;
}
+ void clearArchiveState(String packageName, int userId) {
+ synchronized (mPm.mLock) {
+ PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ ps.setArchiveState(/* archiveState= */ null, userId);
+ }
+ }
+ mPm.mBackgroundHandler.post(
+ () -> {
+ File iconsDir = getIconsDir(packageName, userId);
+ if (!iconsDir.exists()) {
+ return;
+ }
+ // TODO(b/319238030) Move this into installd.
+ if (!FileUtils.deleteContentsAndDir(iconsDir)) {
+ Slog.e(TAG, "Failed to clean up archive files for " + packageName);
+ }
+ });
+ }
+
@Nullable
private String getCurrentLauncherPackageName(int userId) {
ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity(
@@ -437,8 +458,8 @@
if (mainActivity.iconBitmap == null) {
return null;
}
- File iconsDir = createIconsDir(userId);
- File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
+ File iconsDir = createIconsDir(packageName, userId);
+ File iconFile = new File(iconsDir, index + ".png");
try (FileOutputStream out = new FileOutputStream(iconFile)) {
out.write(mainActivity.iconBitmap);
out.flush();
@@ -454,14 +475,14 @@
// The app doesn't define an icon. No need to store anything.
return null;
}
- File iconsDir = createIconsDir(userId);
- File iconFile = new File(iconsDir, packageName + "-" + index + ".png");
+ File iconsDir = createIconsDir(packageName, userId);
+ File iconFile = new File(iconsDir, index + ".png");
Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize);
try (FileOutputStream out = new FileOutputStream(iconFile)) {
// Note: Quality is ignored for PNGs.
if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) {
throw new IOException(TextUtils.formatSimple("Failure to store icon file %s",
- iconFile.getName()));
+ iconFile.getAbsolutePath()));
}
out.flush();
}
@@ -793,8 +814,20 @@
}
@VisibleForTesting
- Bitmap decodeIcon(ArchiveActivityInfo archiveActivityInfo) {
- return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
+ @Nullable
+ Bitmap decodeIcon(ArchiveActivityInfo activityInfo) {
+ Path iconBitmap = activityInfo.getIconBitmap();
+ if (iconBitmap == null) {
+ return null;
+ }
+ Bitmap bitmap = BitmapFactory.decodeFile(iconBitmap.toString());
+ // TODO(b/278553670) We should throw here after some time. Failing graciously now because
+ // we've just changed the place where we store icons.
+ if (bitmap == null) {
+ Slog.e(TAG, "Archived icon cannot be decoded " + iconBitmap.toAbsolutePath());
+ return null;
+ }
+ return bitmap;
}
Bitmap includeCloudOverlay(Bitmap bitmap) {
@@ -1075,8 +1108,9 @@
}
}
- private static File createIconsDir(@UserIdInt int userId) throws IOException {
- File iconsDir = getIconsDir(userId);
+ private static File createIconsDir(String packageName, @UserIdInt int userId)
+ throws IOException {
+ File iconsDir = getIconsDir(packageName, userId);
if (!iconsDir.isDirectory()) {
iconsDir.delete();
iconsDir.mkdirs();
@@ -1088,8 +1122,10 @@
return iconsDir;
}
- private static File getIconsDir(int userId) {
- return new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR);
+ private static File getIconsDir(String packageName, int userId) {
+ return new File(
+ new File(Environment.getDataSystemCeDirectory(userId), ARCHIVE_ICONS_DIR),
+ packageName);
}
private static byte[] bytesFromBitmapFile(Path path) throws IOException {
@@ -1195,7 +1231,7 @@
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class);
if (extraIntent != null && user != null
&& mAppStateHelper.isAppTopVisible(
- getCurrentLauncherPackageName(user.getIdentifier()))) {
+ getCurrentLauncherPackageName(user.getIdentifier()))) {
extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(extraIntent, user);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a9118d4..fdcd28b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -25,6 +25,7 @@
import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED;
import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR;
import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
@@ -1405,7 +1406,7 @@
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
- canSilentlyInstallPackage, userId);
+ canSilentlyInstallPackage, userId, mPackageArchiver, flags);
final boolean shouldShowConfirmationDialog =
(flags & PackageManager.DELETE_SHOW_DIALOG) != 0;
if (!shouldShowConfirmationDialog
@@ -1759,7 +1760,7 @@
binderUid, unarchiveId));
}
- session.reportUnarchivalStatus(unarchiveId, status, requiredStorageBytes,
+ session.reportUnarchivalStatus(status, unarchiveId, requiredStorageBytes,
userActionIntent);
}
}
@@ -1828,9 +1829,23 @@
private final IntentSender mTarget;
private final String mPackageName;
private final Notification mNotification;
+ private final int mUserId;
- public PackageDeleteObserverAdapter(Context context, IntentSender target,
+ @DeleteFlags
+ private final int mFlags;
+
+ @Nullable
+ private final PackageArchiver mPackageArchiver;
+
+ PackageDeleteObserverAdapter(Context context, IntentSender target,
String packageName, boolean showNotification, int userId) {
+ this(context, target, packageName, showNotification, userId,
+ /* packageArchiver= */ null, /* flags= */ 0);
+ }
+
+ PackageDeleteObserverAdapter(Context context, IntentSender target,
+ String packageName, boolean showNotification, int userId,
+ PackageArchiver packageArchiver, @DeleteFlags int flags) {
mContext = context;
mTarget = target;
mPackageName = packageName;
@@ -1842,6 +1857,9 @@
} else {
mNotification = null;
}
+ mUserId = userId;
+ mPackageArchiver = packageArchiver;
+ mFlags = flags;
}
private String getDeviceOwnerDeletedPackageMsg() {
@@ -1883,6 +1901,11 @@
SystemMessage.NOTE_PACKAGE_STATE,
mNotification);
}
+ if (mPackageArchiver != null
+ && PackageManager.DELETE_SUCCEEDED != returnCode
+ && (mFlags & DELETE_ARCHIVE) != 0) {
+ mPackageArchiver.clearArchiveState(mPackageName, mUserId);
+ }
if (mTarget == null) {
return;
}