Loading services/companion/java/com/android/server/companion/datatransfer/continuity/TaskBroadcaster.java +10 −22 Original line number Diff line number Diff line Loading @@ -16,17 +16,12 @@ package com.android.server.companion.datatransfer.continuity; import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray; import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.RemoteException; import android.util.Slog; Loading @@ -36,6 +31,8 @@ import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskA import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskRemovedMessage; import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskUpdatedMessage; import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskInfo; import com.android.server.companion.datatransfer.continuity.tasks.PackageMetadata; import com.android.server.companion.datatransfer.continuity.tasks.PackageMetadataCache; import java.io.IOException; import java.util.List; Loading @@ -54,7 +51,7 @@ class TaskBroadcaster extends TaskStackListener { private final Context mContext; private final ActivityTaskManager mActivityTaskManager; private final TaskContinuityMessenger mTaskContinuityMessenger; private final PackageManager mPackageManager; private final PackageMetadataCache mPackageMetadataCache; private boolean mIsListeningToActivityTaskManager = false; Loading @@ -67,7 +64,7 @@ class TaskBroadcaster extends TaskStackListener { mContext = context; mActivityTaskManager = context.getSystemService(ActivityTaskManager.class); mPackageManager = context.getPackageManager(); mPackageMetadataCache = new PackageMetadataCache(context.getPackageManager()); mTaskContinuityMessenger = taskContinuityMessenger; } Loading Loading @@ -166,26 +163,17 @@ class TaskBroadcaster extends TaskStackListener { } private RemoteTaskInfo createRemoteTaskInfo(RunningTaskInfo taskInfo) { PackageInfo packageInfo; try { packageInfo = mPackageManager.getPackageInfo( taskInfo.baseActivity.getPackageName(), PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Failed to get package info for task: " + taskInfo.taskId, e); PackageMetadata packageMetadata = mPackageMetadataCache.getMetadataForPackage( taskInfo.baseActivity.getPackageName()); if (packageMetadata == null) { Slog.w(TAG, "Could not get package metadata for task: " + taskInfo.taskId); return null; } String baseApplicationLabel = mPackageManager.getApplicationLabel( packageInfo.applicationInfo).toString(); Drawable baseApplicationIcon = mPackageManager.getApplicationIcon( packageInfo.applicationInfo); return new RemoteTaskInfo( taskInfo.taskId, baseApplicationLabel, packageMetadata.label(), taskInfo.lastActiveTime, renderDrawableToByteArray(baseApplicationIcon)); packageMetadata.icon()); } } No newline at end of file services/companion/java/com/android/server/companion/datatransfer/continuity/tasks/PackageMetadata.java 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.companion.datatransfer.continuity.tasks; import android.annotation.NonNull; /** * Metadata about a package. * * @param label The label of the package. * @param icon The icon of the package. */ public record PackageMetadata(@NonNull String label, @NonNull byte[] icon) {} No newline at end of file services/companion/java/com/android/server/companion/datatransfer/continuity/tasks/PackageMetadataCache.java 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.companion.datatransfer.continuity.tasks; import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.graphics.drawable.Drawable; import android.util.Slog; import java.util.HashMap; import java.util.Map; import java.util.Objects; import javax.annotation.concurrent.GuardedBy; public class PackageMetadataCache { private static final String TAG = "PackageMetadataCache"; private final PackageManager mPackageManager; @GuardedBy("this") private final Map<String, PackageMetadata> mPackageMetadataMap = new HashMap<>(); public PackageMetadataCache(@NonNull PackageManager packageManager) { mPackageManager = Objects.requireNonNull(packageManager); } @Nullable public PackageMetadata getMetadataForPackage(@NonNull String packageName) { Objects.requireNonNull(packageName); synchronized (this) { if (mPackageMetadataMap.containsKey(packageName)) { return mPackageMetadataMap.get(packageName); } PackageInfo packageInfo; try { packageInfo = mPackageManager.getPackageInfo( packageName, PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Failed to get package info for package: " + packageName, e); return null; } CharSequence label = mPackageManager.getApplicationLabel(packageInfo.applicationInfo); if (label == null) { Slog.e(TAG, "PackageManager returned null label for package: " + packageName); return null; } Drawable icon = mPackageManager.getApplicationIcon(packageInfo.applicationInfo); if (icon == null) { Slog.e(TAG, "PackageManager returned null icon for package: " + packageName); return null; } byte[] serializedIcon = renderDrawableToByteArray(icon); if (serializedIcon == null) { Slog.e(TAG, "Failed to serialize icon for package: " + packageName); return null; } PackageMetadata metadata = new PackageMetadata(label.toString(), serializedIcon); mPackageMetadataMap.put(packageName, metadata); return metadata; } } } No newline at end of file services/tests/servicestests/src/com/android/server/companion/datatransfer/continuity/tasks/PackageMetadataCacheTest.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.companion.datatransfer.continuity.tasks; import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.mockito.ArgumentMatchers.eq; import com.android.frameworks.servicestests.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.platform.test.annotations.Presubmit; import android.testing.AndroidTestingRunner; import androidx.test.InstrumentationRegistry; @Presubmit @RunWith(AndroidTestingRunner.class) public class PackageMetadataCacheTest { @Mock private PackageManager mockPackageManager; private PackageMetadataCache packageMetadataCache; @Before public void setUp() { MockitoAnnotations.initMocks(this); packageMetadataCache = new PackageMetadataCache(mockPackageManager); } @Test public void testGetMetadataForPackage_returnsMetadata() { String packageName = "com.example.app"; String label = "label"; Drawable icon = createTestDrawable(); setupMockApplicationInfo(packageName, label, icon); PackageMetadata metadata = packageMetadataCache.getMetadataForPackage(packageName); assertThat(metadata.label()).isEqualTo(label); assertThat(metadata.icon()).isEqualTo(renderDrawableToByteArray(icon)); } @Test public void testGetMetadataForPackage_packageManagerReturnsNullLabel_returnsNull() { String packageName = "com.example.app"; Drawable icon = createTestDrawable(); setupMockApplicationInfo(packageName, null, icon); assertThat(packageMetadataCache.getMetadataForPackage(packageName)).isNull(); } @Test public void testGetMetadataForPackage_packageManagerReturnsNullIcon_returnsNull() { String packageName = "com.example.app"; String label = "label"; setupMockApplicationInfo(packageName, label, null); assertThat(packageMetadataCache.getMetadataForPackage(packageName)).isNull(); } @Test public void testGetMetadataForPackage_packageManagerThrowsException_returnsNull() throws PackageManager.NameNotFoundException { when(mockPackageManager.getPackageInfo(anyString(), anyInt())) .thenThrow(new PackageManager.NameNotFoundException()); assertThat(packageMetadataCache.getMetadataForPackage("com.example.app")).isNull(); } private void setupMockApplicationInfo(String packageName, String label, Drawable icon) { PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = packageName; packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.name = packageName; try { when(mockPackageManager .getPackageInfo(eq(packageName), eq(PackageManager.GET_META_DATA))) .thenReturn(packageInfo); } catch (PackageManager.NameNotFoundException e) { } when(mockPackageManager.getApplicationLabel(eq(packageInfo.applicationInfo))) .thenReturn(label); when(mockPackageManager.getApplicationIcon(eq(packageInfo.applicationInfo))) .thenReturn(icon); } private Drawable createTestDrawable() { Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); Bitmap bitmap = BitmapFactory.decodeResource( context.getResources(), R.drawable.black_32x32); return new BitmapDrawable(context.getResources(), bitmap); } } No newline at end of file Loading
services/companion/java/com/android/server/companion/datatransfer/continuity/TaskBroadcaster.java +10 −22 Original line number Diff line number Diff line Loading @@ -16,17 +16,12 @@ package com.android.server.companion.datatransfer.continuity; import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray; import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.RemoteException; import android.util.Slog; Loading @@ -36,6 +31,8 @@ import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskA import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskRemovedMessage; import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskUpdatedMessage; import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskInfo; import com.android.server.companion.datatransfer.continuity.tasks.PackageMetadata; import com.android.server.companion.datatransfer.continuity.tasks.PackageMetadataCache; import java.io.IOException; import java.util.List; Loading @@ -54,7 +51,7 @@ class TaskBroadcaster extends TaskStackListener { private final Context mContext; private final ActivityTaskManager mActivityTaskManager; private final TaskContinuityMessenger mTaskContinuityMessenger; private final PackageManager mPackageManager; private final PackageMetadataCache mPackageMetadataCache; private boolean mIsListeningToActivityTaskManager = false; Loading @@ -67,7 +64,7 @@ class TaskBroadcaster extends TaskStackListener { mContext = context; mActivityTaskManager = context.getSystemService(ActivityTaskManager.class); mPackageManager = context.getPackageManager(); mPackageMetadataCache = new PackageMetadataCache(context.getPackageManager()); mTaskContinuityMessenger = taskContinuityMessenger; } Loading Loading @@ -166,26 +163,17 @@ class TaskBroadcaster extends TaskStackListener { } private RemoteTaskInfo createRemoteTaskInfo(RunningTaskInfo taskInfo) { PackageInfo packageInfo; try { packageInfo = mPackageManager.getPackageInfo( taskInfo.baseActivity.getPackageName(), PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Failed to get package info for task: " + taskInfo.taskId, e); PackageMetadata packageMetadata = mPackageMetadataCache.getMetadataForPackage( taskInfo.baseActivity.getPackageName()); if (packageMetadata == null) { Slog.w(TAG, "Could not get package metadata for task: " + taskInfo.taskId); return null; } String baseApplicationLabel = mPackageManager.getApplicationLabel( packageInfo.applicationInfo).toString(); Drawable baseApplicationIcon = mPackageManager.getApplicationIcon( packageInfo.applicationInfo); return new RemoteTaskInfo( taskInfo.taskId, baseApplicationLabel, packageMetadata.label(), taskInfo.lastActiveTime, renderDrawableToByteArray(baseApplicationIcon)); packageMetadata.icon()); } } No newline at end of file
services/companion/java/com/android/server/companion/datatransfer/continuity/tasks/PackageMetadata.java 0 → 100644 +27 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.companion.datatransfer.continuity.tasks; import android.annotation.NonNull; /** * Metadata about a package. * * @param label The label of the package. * @param icon The icon of the package. */ public record PackageMetadata(@NonNull String label, @NonNull byte[] icon) {} No newline at end of file
services/companion/java/com/android/server/companion/datatransfer/continuity/tasks/PackageMetadataCache.java 0 → 100644 +88 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.companion.datatransfer.continuity.tasks; import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.PackageInfo; import android.graphics.drawable.Drawable; import android.util.Slog; import java.util.HashMap; import java.util.Map; import java.util.Objects; import javax.annotation.concurrent.GuardedBy; public class PackageMetadataCache { private static final String TAG = "PackageMetadataCache"; private final PackageManager mPackageManager; @GuardedBy("this") private final Map<String, PackageMetadata> mPackageMetadataMap = new HashMap<>(); public PackageMetadataCache(@NonNull PackageManager packageManager) { mPackageManager = Objects.requireNonNull(packageManager); } @Nullable public PackageMetadata getMetadataForPackage(@NonNull String packageName) { Objects.requireNonNull(packageName); synchronized (this) { if (mPackageMetadataMap.containsKey(packageName)) { return mPackageMetadataMap.get(packageName); } PackageInfo packageInfo; try { packageInfo = mPackageManager.getPackageInfo( packageName, PackageManager.GET_META_DATA); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Failed to get package info for package: " + packageName, e); return null; } CharSequence label = mPackageManager.getApplicationLabel(packageInfo.applicationInfo); if (label == null) { Slog.e(TAG, "PackageManager returned null label for package: " + packageName); return null; } Drawable icon = mPackageManager.getApplicationIcon(packageInfo.applicationInfo); if (icon == null) { Slog.e(TAG, "PackageManager returned null icon for package: " + packageName); return null; } byte[] serializedIcon = renderDrawableToByteArray(icon); if (serializedIcon == null) { Slog.e(TAG, "Failed to serialize icon for package: " + packageName); return null; } PackageMetadata metadata = new PackageMetadata(label.toString(), serializedIcon); mPackageMetadataMap.put(packageName, metadata); return metadata; } } } No newline at end of file
services/tests/servicestests/src/com/android/server/companion/datatransfer/continuity/tasks/PackageMetadataCacheTest.java 0 → 100644 +122 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.companion.datatransfer.continuity.tasks; import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import static org.mockito.ArgumentMatchers.eq; import com.android.frameworks.servicestests.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.platform.test.annotations.Presubmit; import android.testing.AndroidTestingRunner; import androidx.test.InstrumentationRegistry; @Presubmit @RunWith(AndroidTestingRunner.class) public class PackageMetadataCacheTest { @Mock private PackageManager mockPackageManager; private PackageMetadataCache packageMetadataCache; @Before public void setUp() { MockitoAnnotations.initMocks(this); packageMetadataCache = new PackageMetadataCache(mockPackageManager); } @Test public void testGetMetadataForPackage_returnsMetadata() { String packageName = "com.example.app"; String label = "label"; Drawable icon = createTestDrawable(); setupMockApplicationInfo(packageName, label, icon); PackageMetadata metadata = packageMetadataCache.getMetadataForPackage(packageName); assertThat(metadata.label()).isEqualTo(label); assertThat(metadata.icon()).isEqualTo(renderDrawableToByteArray(icon)); } @Test public void testGetMetadataForPackage_packageManagerReturnsNullLabel_returnsNull() { String packageName = "com.example.app"; Drawable icon = createTestDrawable(); setupMockApplicationInfo(packageName, null, icon); assertThat(packageMetadataCache.getMetadataForPackage(packageName)).isNull(); } @Test public void testGetMetadataForPackage_packageManagerReturnsNullIcon_returnsNull() { String packageName = "com.example.app"; String label = "label"; setupMockApplicationInfo(packageName, label, null); assertThat(packageMetadataCache.getMetadataForPackage(packageName)).isNull(); } @Test public void testGetMetadataForPackage_packageManagerThrowsException_returnsNull() throws PackageManager.NameNotFoundException { when(mockPackageManager.getPackageInfo(anyString(), anyInt())) .thenThrow(new PackageManager.NameNotFoundException()); assertThat(packageMetadataCache.getMetadataForPackage("com.example.app")).isNull(); } private void setupMockApplicationInfo(String packageName, String label, Drawable icon) { PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = packageName; packageInfo.applicationInfo = new ApplicationInfo(); packageInfo.applicationInfo.name = packageName; try { when(mockPackageManager .getPackageInfo(eq(packageName), eq(PackageManager.GET_META_DATA))) .thenReturn(packageInfo); } catch (PackageManager.NameNotFoundException e) { } when(mockPackageManager.getApplicationLabel(eq(packageInfo.applicationInfo))) .thenReturn(label); when(mockPackageManager.getApplicationIcon(eq(packageInfo.applicationInfo))) .thenReturn(icon); } private Drawable createTestDrawable() { Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); Bitmap bitmap = BitmapFactory.decodeResource( context.getResources(), R.drawable.black_32x32); return new BitmapDrawable(context.getResources(), bitmap); } } No newline at end of file