Merge "Report MediaProjectionTargetChanged Atom" into main
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 50f9bc4..dd2a104 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3964,9 +3964,15 @@
// on a different thread. However, when the current process is system, the finishDraw in
// system server will be run on the current thread, which could result in a deadlock.
if (mWindowSession instanceof Binder) {
- reportDrawFinished(t, seqId);
+ // The transaction should be copied to a local reference when posting onto a new
+ // thread because up until now the SSG is holding a lock on the transaction. Once
+ // the call jumps onto a new thread, the lock is no longer held and the transaction
+ // send back may be modified or used again.
+ Transaction transactionCopy = new Transaction();
+ transactionCopy.merge(t);
+ mHandler.postAtFrontOfQueue(() -> reportDrawFinished(transactionCopy, seqId));
} else {
- mHandler.postAtFrontOfQueue(() -> reportDrawFinished(t, seqId));
+ reportDrawFinished(t, seqId);
}
});
if (DEBUG_BLAST) {
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 0b69030..9d88a23 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -401,6 +401,12 @@
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
mChangeType = PACKAGE_UPDATING;
onPackageUpdateStarted(pkg, uid);
+ if (intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false)) {
+ // In case it is a removal event due to archiving, we trigger package
+ // update event to refresh details like icons, title etc. corresponding to
+ // the archived app.
+ onPackageModified(pkg);
+ }
} else {
mChangeType = PACKAGE_PERMANENT_CHANGE;
// We only consider something to have changed if this is
diff --git a/core/res/res/drawable/archived_app_cloud_overlay.xml b/core/res/res/drawable/archived_app_cloud_overlay.xml
new file mode 100644
index 0000000..611e0f3
--- /dev/null
+++ b/core/res/res/drawable/archived_app_cloud_overlay.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+ <group
+ android:scaleX="1.2"
+ android:scaleY="1.2"
+ android:translateX="15"
+ android:translateY="14">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18L6,18c-2.21,0 -4,-1.79 -4,-4 0,-2.05 1.53,-3.76 3.56,-3.97l1.07,-0.11 0.5,-0.95C8.08,7.14 9.94,6 12,6c2.62,0 4.88,1.86 5.39,4.43l0.3,1.5 1.53,0.11c1.56,0.1 2.78,1.41 2.78,2.96 0,1.65 -1.35,3 -3,3zM13.45,10h-2.9v3L8,13l4,4 4,-4h-2.55z"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 14bbb96..1aa1fea 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1405,6 +1405,7 @@
<java-symbol type="drawable" name="ic_test_badge_no_background" />
<java-symbol type="drawable" name="ic_test_icon_badge_experiment" />
<java-symbol type="drawable" name="ic_instant_icon_badge_bolt" />
+ <java-symbol type="drawable" name="archived_app_cloud_overlay" />
<java-symbol type="drawable" name="emulator_circular_window_overlay" />
<java-symbol type="drawable" name="ic_qs_battery_saver" />
<java-symbol type="drawable" name="ic_qs_bluetooth" />
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index a339907..8e653f5 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -298,6 +298,43 @@
}
@Test
+ public void testPackageMonitorDoHandlePackageEventPackageRemovedReplacingArchived() {
+ PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+ intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+ intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ intent.putExtra(Intent.EXTRA_ARCHIVAL, true);
+ intent.putExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, true);
+ spyPackageMonitor.doHandlePackageEvent(intent);
+
+ verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+ verify(spyPackageMonitor, times(1))
+ .onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+ verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+ ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(spyPackageMonitor, times(1))
+ .onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME), argumentCaptor.capture());
+ Bundle capturedExtras = argumentCaptor.getValue();
+ Bundle expectedExtras = intent.getExtras();
+ assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+ assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+ .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
+ verify(spyPackageMonitor, times(1))
+ .onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
+ verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+ }
+
+ @Test
public void testPackageMonitorDoHandlePackageEventPackageRemovedNotReplacing()
throws Exception {
PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index a4a9290..adebdcd 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -33,6 +33,7 @@
],
static_libs: [
"device_config_service_flags_java",
+ "libaconfig_java_proto_lite",
"SettingsLibDeviceStateRotationLock",
"SettingsLibDisplayUtils",
],
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 969f1fd..976ba21 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -22,6 +22,8 @@
import static com.android.providers.settings.Flags.supportOverrides;
+import android.aconfig.Aconfig.parsed_flag;
+import android.aconfig.Aconfig.parsed_flags;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.AttributionSource;
@@ -39,12 +41,13 @@
import android.provider.Settings;
import android.provider.Settings.Config.SyncDisabledMode;
import android.provider.UpdatableDeviceConfigServiceReadiness;
+import android.util.Slog;
import com.android.internal.util.FastPrintWriter;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -56,18 +59,17 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Scanner;
/**
* Receives shell commands from the command line related to device config flags, and dispatches them
* to the SettingsProvider.
*/
public final class DeviceConfigService extends Binder {
- private static final List<String> aconfigTextProtoFilesOnDevice = List.of(
- "/system/etc/aconfig_flags.textproto",
- "/system_ext/etc/aconfig_flags.textproto",
- "/system_ext/etc/aconfig_flags.textproto",
- "/vendor/etc/aconfig_flags.textproto");
+ private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
+ "/system/etc/aconfig_flags.pb",
+ "/system_ext/etc/aconfig_flags.pb",
+ "/system_ext/etc/aconfig_flags.pb",
+ "/vendor/etc/aconfig_flags.pb");
private static final List<String> PRIVATE_NAMESPACES = List.of(
"device_config_overrides",
@@ -76,6 +78,8 @@
final SettingsProvider mProvider;
+ private static final String TAG = "DeviceConfigService";
+
public DeviceConfigService(SettingsProvider provider) {
mProvider = provider;
}
@@ -94,62 +98,55 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- final IContentProvider iprovider = mProvider.getIContentProvider();
- pw.println("DeviceConfig flags:");
- for (String line : MyShellCommand.listAll(iprovider)) {
- pw.println(line);
- }
+ final IContentProvider iprovider = mProvider.getIContentProvider();
+ pw.println("DeviceConfig flags:");
+ for (String line : MyShellCommand.listAll(iprovider)) {
+ pw.println(line);
+ }
- ArrayList<String> missingFiles = new ArrayList<String>();
- for (String fileName : aconfigTextProtoFilesOnDevice) {
- File aconfigFile = new File(fileName);
- if (!aconfigFile.exists()) {
- missingFiles.add(fileName);
+ ArrayList<String> missingFiles = new ArrayList<String>();
+ for (String fileName : sAconfigTextProtoFilesOnDevice) {
+ File aconfigFile = new File(fileName);
+ if (!aconfigFile.exists()) {
+ missingFiles.add(fileName);
+ }
}
- }
- if (missingFiles.isEmpty()) {
- pw.println("\nAconfig flags:");
- for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
- pw.println(name);
+ if (missingFiles.isEmpty()) {
+ pw.println("\nAconfig flags:");
+ for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
+ pw.println(name);
+ }
+ } else {
+ pw.println("\nFailed to dump aconfig flags due to missing files:");
+ for (String fileName : missingFiles) {
+ pw.println(fileName);
+ }
}
- } else {
- pw.println("\nFailed to dump aconfig flags due to missing files:");
- for (String fileName : missingFiles) {
- pw.println(fileName);
- }
- }
}
private static HashSet<String> getAconfigFlagNamesInDeviceConfig() {
HashSet<String> nameSet = new HashSet<String>();
- for (String fileName : aconfigTextProtoFilesOnDevice) {
- try{
- File aconfigFile = new File(fileName);
- String packageName = "";
- String namespace = "";
- String name = "";
-
- try (Scanner scanner = new Scanner(aconfigFile)) {
- while (scanner.hasNextLine()) {
- String data = scanner.nextLine().replaceAll("\\s+","");
- if (data.startsWith("package:\"")) {
- packageName = data.substring(9, data.length()-1);
- } else if (data.startsWith("name:\"")) {
- name = data.substring(6, data.length()-1);
- } else if (data.startsWith("namespace:\"")) {
- namespace = data.substring(11, data.length()-1);
- nameSet.add(namespace + "/" + packageName + "." + name);
+ try {
+ for (String fileName : sAconfigTextProtoFilesOnDevice) {
+ byte[] contents = (new FileInputStream(fileName)).readAllBytes();
+ parsed_flags parsedFlags = parsed_flags.parseFrom(contents);
+ if (parsedFlags == null) {
+ Slog.e(TAG, "failed to parse aconfig protobuf from " + fileName);
+ continue;
}
- }
+
+ for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
+ String namespace = flag.getNamespace();
+ String packageName = flag.getPackage();
+ String name = flag.getName();
+ nameSet.add(namespace + "/" + packageName + "." + name);
+ }
}
-
- } catch (FileNotFoundException e) {
- continue;
- }
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to read aconfig protobuf", e);
}
-
- return nameSet;
+ return nameSet;
}
private void callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out,
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 42a97f7..a296160 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -46,6 +46,12 @@
import android.content.pm.VersionedPackage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -56,6 +62,7 @@
import android.text.TextUtils;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.pkg.ArchiveState;
@@ -367,7 +374,7 @@
// TODO(b/298452477) Handle monochrome icons.
// In the rare case the archived app defined more than two launcher activities, we choose
// the first one arbitrarily.
- return decodeIcon(activityInfos.get(0));
+ return includeCloudOverlay(decodeIcon(activityInfos.get(0)));
}
@VisibleForTesting
@@ -375,6 +382,34 @@
return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
}
+ Bitmap includeCloudOverlay(Bitmap bitmap) {
+ Drawable cloudDrawable =
+ mContext.getResources()
+ .getDrawable(R.drawable.archived_app_cloud_overlay, mContext.getTheme());
+ if (cloudDrawable == null) {
+ Slog.e(TAG, "Unable to locate cloud overlay for archived app!");
+ return bitmap;
+ }
+ BitmapDrawable appIconDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
+ PorterDuffColorFilter colorFilter =
+ new PorterDuffColorFilter(
+ Color.argb(0.32f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */),
+ PorterDuff.Mode.SRC_ATOP);
+ appIconDrawable.setColorFilter(colorFilter);
+ appIconDrawable.setBounds(
+ 0 /* left */,
+ 0 /* top */,
+ cloudDrawable.getIntrinsicWidth(),
+ cloudDrawable.getIntrinsicHeight());
+ LayerDrawable layerDrawable =
+ new LayerDrawable(new Drawable[] {appIconDrawable, cloudDrawable});
+ final int iconSize = mContext.getSystemService(
+ ActivityManager.class).getLauncherLargeIconSize();
+ Bitmap appIconWithCloudOverlay = drawableToBitmap(layerDrawable, iconSize);
+ bitmap.recycle();
+ return appIconWithCloudOverlay;
+ }
+
private void verifyArchived(PackageStateInternal ps, int userId)
throws PackageManager.NameNotFoundException {
PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
diff --git a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java
index c2e819e..4d4f99f 100644
--- a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java
@@ -20,7 +20,6 @@
import android.provider.DeviceConfig;
import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -98,27 +97,28 @@
* @throws IllegalArgumentException {@code key} isn't recognised.
*/
boolean getFlagValue(@NonNull String key) {
- return findEntry(key).map(SynchedDeviceConfigEntry::getValue)
- .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key));
+ final SynchedDeviceConfigEntry entry = mDeviceConfigEntries.get(key);
+ if (entry == null) {
+ throw new IllegalArgumentException("Unexpected flag name: " + key);
+ }
+ return entry.getValue();
}
/**
* @return {@code true} if the flag for the given {@code key} was enabled at build time.
*/
boolean isBuildTimeFlagEnabled(@NonNull String key) {
- return findEntry(key).map(SynchedDeviceConfigEntry::isBuildTimeFlagEnabled)
- .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key));
+ final SynchedDeviceConfigEntry entry = mDeviceConfigEntries.get(key);
+ if (entry == null) {
+ throw new IllegalArgumentException("Unexpected flag name: " + key);
+ }
+ return entry.isBuildTimeFlagEnabled();
}
private boolean isDeviceConfigFlagEnabled(@NonNull String key, boolean defaultValue) {
return DeviceConfig.getBoolean(mNamespace, key, defaultValue);
}
- @NonNull
- private Optional<SynchedDeviceConfigEntry> findEntry(@NonNull String key) {
- return Optional.ofNullable(mDeviceConfigEntries.get(key));
- }
-
static class SynchedDeviceConfigBuilder {
private final String mNamespace;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index e7f1d16e..5c8a19c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -182,6 +182,10 @@
any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
doReturn(mIcon).when(mArchiveManager).decodeIcon(
any(ArchiveState.ArchiveActivityInfo.class));
+ Resources mockResources = mock(Resources.class);
+ doReturn(mockResources)
+ .when(mContext)
+ .getResources();
}
@Test