Loading core/res/res/values/config.xml +6 −6 Original line number Original line Diff line number Diff line Loading @@ -3959,14 +3959,14 @@ <!-- Component name for the default module metadata provider on this device --> <!-- Component name for the default module metadata provider on this device --> <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string> <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string> <!-- This is the default launcher component to use on secondary displays that support system <!-- This is the default launcher package with an activity to use on secondary displays that decorations. support system decorations. This launcher activity must support multiple instances and have corresponding launch mode This launcher package must have an activity that supports multiple instances and has set in AndroidManifest. corresponding launch mode set in AndroidManifest. {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string> <string name="config_secondaryHomePackage" translatable="false">com.android.launcher3</string> <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is <!-- Force secondary home launcher specified in config_secondaryHomePackage always. If this is not set, secondary home launcher can be replaced by user. --> not set, secondary home launcher can be replaced by user. --> <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool> <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool> Loading core/res/res/values/symbols.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -3678,7 +3678,7 @@ <java-symbol type="string" name="config_defaultModuleMetadataProvider" /> <java-symbol type="string" name="config_defaultModuleMetadataProvider" /> <!-- For Secondary Launcher --> <!-- For Secondary Launcher --> <java-symbol type="string" name="config_secondaryHomeComponent" /> <java-symbol type="string" name="config_secondaryHomePackage" /> <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" /> <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" /> <java-symbol type="string" name="battery_saver_notification_channel_name" /> <java-symbol type="string" name="battery_saver_notification_channel_name" /> Loading services/core/java/com/android/server/wm/ActivityTaskManagerService.java +6 −6 Original line number Original line Diff line number Diff line Loading @@ -5868,8 +5868,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home * activities. * activities. * * * @param preferredPackage Specify a preferred package name, otherwise use secondary home * @param preferredPackage Specify a preferred package name, otherwise use the package name * component defined in config_secondaryHomeComponent. * defined in config_secondaryHomePackage. * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} */ */ Intent getSecondaryHomeIntent(String preferredPackage) { Intent getSecondaryHomeIntent(String preferredPackage) { Loading @@ -5877,10 +5877,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean( final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); if (preferredPackage == null || useSystemProvidedLauncher) { if (preferredPackage == null || useSystemProvidedLauncher) { // Using the component stored in config if no package name or forced. // Using the package name stored in config if no preferred package name or forced. final String secondaryHomeComponent = mContext.getResources().getString( final String secondaryHomePackage = mContext.getResources().getString( com.android.internal.R.string.config_secondaryHomeComponent); com.android.internal.R.string.config_secondaryHomePackage); intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent)); intent.setPackage(secondaryHomePackage); } else { } else { intent.setPackage(preferredPackage); intent.setPackage(preferredPackage); } } Loading services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +98 −129 Original line number Original line Diff line number Diff line Loading @@ -52,7 +52,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.refEq; import static org.mockito.ArgumentMatchers.refEq; import android.app.ActivityOptions; import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Intent; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo; Loading Loading @@ -517,7 +516,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ */ @Test @Test public void testStartHomeOnAllDisplays() { public void testStartHomeOnAllDisplays() { mockResolveHomeActivity(); mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); mockResolveSecondaryHomeActivity(); mockResolveSecondaryHomeActivity(); // Create secondary displays. // Create secondary displays. Loading Loading @@ -644,30 +643,26 @@ public class RootActivityContainerTests extends ActivityTestsBase { } } /** /** * Tests that secondary home should be selected if default home not set. * Tests that secondary home should be selected if primary home not set. */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() { public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() { final Intent defaultHomeIntent = mService.getHomeIntent(); // Setup: primary home not set. final ActivityInfo aInfoDefault = new ActivityInfo(); final Intent primaryHomeIntent = mService.getHomeIntent(); aInfoDefault.name = ResolverActivity.class.getName(); final ActivityInfo aInfoPrimary = new ActivityInfo(); doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), aInfoPrimary.name = ResolverActivity.class.getName(); refEq(defaultHomeIntent)); doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(primaryHomeIntent)); final String secondaryHomeComponent = mService.mContext.getResources().getString( // Setup: set secondary home. com.android.internal.R.string.config_secondaryHomeComponent); mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); // Run the test. final ActivityInfo aInfoSecondary = new ActivityInfo(); aInfoSecondary.name = comp.getClassName(); doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(secondaryHomeIntent)); // Should fallback to secondary home if default home not set. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); assertEquals(comp.getClassName(), resolvedInfo.first.name); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); assertEquals(aInfoSecondary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); } } /** /** Loading @@ -675,103 +670,60 @@ public class RootActivityContainerTests extends ActivityTestsBase { * config_useSystemProvidedLauncherForSecondary. * config_useSystemProvidedLauncherForSecondary. */ */ @Test @Test public void testResolveSecondaryHomeActivityForced() throws Exception { public void testResolveSecondaryHomeActivityForced() { Resources resources = mContext.getResources(); // SetUp: set primary home. spyOn(resources); mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); try { // SetUp: set secondary home and force it. // setUp: set secondary launcher and force it. mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */); final String defaultSecondaryHome = final Intent secondaryHomeIntent = "com.android.test/com.android.test.TestDefaultSecondaryHome"; mService.getSecondaryHomeIntent(null /* preferredPackage */); final ComponentName secondaryComp = ComponentName.unflattenFromString( final List<ResolveInfo> resolutions = new ArrayList<>(); defaultSecondaryHome); final ResolveInfo resolveInfo = new ResolveInfo(); doReturn(defaultSecondaryHome).when(resources).getString( final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); com.android.internal.R.string.config_secondaryHomeComponent); resolveInfo.activityInfo = aInfoSecondary; doReturn(true).when(resources).getBoolean( resolutions.add(resolveInfo); com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); assertEquals(secondaryComp, secondaryHomeIntent.getComponent()); final ActivityInfo aInfoSecondary = new ActivityInfo(); aInfoSecondary.name = secondaryComp.getClassName(); aInfoSecondary.applicationInfo = new ApplicationInfo(); aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName(); doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(secondaryHomeIntent)); final Intent homeIntent = mService.getHomeIntent(); final ActivityInfo aInfoDefault = new ActivityInfo(); aInfoDefault.name = "fakeHomeActivity"; aInfoDefault.applicationInfo = new ApplicationInfo(); aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(homeIntent)); // Let resolveActivities call to validate both main launcher and second launcher so that // resolveActivities call does not work as enabler for secondary. final List<ResolveInfo> resolutions1 = new ArrayList<>(); final ResolveInfo resolveInfo1 = new ResolveInfo(); resolveInfo1.activityInfo = new ActivityInfo(); resolveInfo1.activityInfo.name = aInfoDefault.name; resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo; resolutions1.add(resolveInfo1); doReturn(resolutions1).when(mRootWindowContainer).resolveActivities(anyInt(), refEq(homeIntent)); final List<ResolveInfo> resolutions2 = new ArrayList<>(); final ResolveInfo resolveInfo2 = new ResolveInfo(); resolveInfo2.activityInfo = new ActivityInfo(); resolveInfo2.activityInfo.name = aInfoSecondary.name; resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo; resolutions2.add(resolveInfo2); doReturn(resolutions2).when(mRootWindowContainer).resolveActivities(anyInt(), refEq(secondaryHomeIntent)); refEq(secondaryHomeIntent)); doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean()); // Run the test // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name); assertEquals(secondaryComp.getPackageName(), resolvedInfo.first.applicationInfo.packageName); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); } finally { assertEquals(aInfoSecondary.applicationInfo.packageName, // tearDown resolvedInfo.first.applicationInfo.packageName); reset(resources); } } } /** /** * Tests that secondary home should be selected if default home not support secondary displays * Tests that secondary home should be selected if primary home not support secondary displays * or there is no matched activity in the same package as selected default home. * or there is no matched activity in the same package as selected primary home. */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() { mockResolveHomeActivity(); // Setup: there is no matched activity in the same package as selected primary home. mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); final List<ResolveInfo> resolutions = new ArrayList<>(); final List<ResolveInfo> resolutions = new ArrayList<>(); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); // Setup: set secondary home. mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); final String secondaryHomeComponent = mService.mContext.getResources().getString( // Run the test. com.android.internal.R.string.config_secondaryHomeComponent); final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); final ActivityInfo aInfoSecondary = new ActivityInfo(); aInfoSecondary.name = comp.getClassName(); doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(secondaryHomeIntent)); // Should fallback to secondary home if selected default home not support secondary displays // or there is no matched activity in the same package as selected default home. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); assertEquals(comp.getClassName(), resolvedInfo.first.name); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); assertEquals(aInfoSecondary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); } } /** /** * Tests that default home activity should be selected if it already support secondary displays. * Tests that primary home activity should be selected if it already support secondary displays. */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() { final ActivityInfo aInfoDefault = mockResolveHomeActivity(); // SetUp: set primary home. mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); // SetUp: put primary home info on 2nd item final List<ResolveInfo> resolutions = new ArrayList<>(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); final ResolveInfo infoFake1 = new ResolveInfo(); infoFake1.activityInfo = new ActivityInfo(); infoFake1.activityInfo = new ActivityInfo(); Loading @@ -779,7 +731,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; final ResolveInfo infoFake2 = new ResolveInfo(); final ResolveInfo infoFake2 = new ResolveInfo(); infoFake2.activityInfo = aInfoDefault; final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */); infoFake2.activityInfo = aInfoPrimary; resolutions.add(infoFake1); resolutions.add(infoFake1); resolutions.add(infoFake2); resolutions.add(infoFake2); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); Loading @@ -787,13 +740,12 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean()); // Use default home activity if it support secondary displays. // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); assertEquals(aInfoPrimary.name, resolvedInfo.first.name); assertEquals(aInfoDefault.applicationInfo.packageName, assertEquals(aInfoPrimary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); resolvedInfo.first.applicationInfo.packageName); assertEquals(aInfoDefault.name, resolvedInfo.first.name); } } /** /** Loading @@ -801,8 +753,9 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { mockResolveHomeActivity(); // SetUp: set primary home. mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); // Setup: prepare two eligible activity info. final List<ResolveInfo> resolutions = new ArrayList<>(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); final ResolveInfo infoFake1 = new ResolveInfo(); infoFake1.activityInfo = new ActivityInfo(); infoFake1.activityInfo = new ActivityInfo(); Loading @@ -821,7 +774,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean()); // Use the first one of matched activities in the same package as selected default home. // Use the first one of matched activities in the same package as selected primary home. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); Loading Loading @@ -884,32 +837,48 @@ public class RootActivityContainerTests extends ActivityTestsBase { /** /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases (the original implementation will resolve from the real package manager). * info for test cases. * * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use * secondary home intent. * @param forceSystemProvided Indicate to force using system provided home activity. */ */ private ActivityInfo mockResolveHomeActivity() { private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) { final Intent homeIntent = mService.getHomeIntent(); ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome); final ActivityInfo aInfoDefault = new ActivityInfo(); Intent targetIntent; aInfoDefault.name = "fakeHomeActivity"; if (primaryHome) { aInfoDefault.applicationInfo = new ApplicationInfo(); targetIntent = mService.getHomeIntent(); aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; } else { doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), Resources resources = mContext.getResources(); refEq(homeIntent)); spyOn(resources); return aInfoDefault; doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString( com.android.internal.R.string.config_secondaryHomePackage); doReturn(forceSystemProvided).when(resources).getBoolean( com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); targetIntent = mService.getSecondaryHomeIntent(null /* preferredPackage */); } doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(targetIntent)); } } /** /** * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent * activity info for test cases (the original implementation will resolve from the real package * activity info for test cases. * manager). */ */ private void mockResolveSecondaryHomeActivity() { private void mockResolveSecondaryHomeActivity() { final Intent secondaryHomeIntent = mService final Intent secondaryHomeIntent = mService .getSecondaryHomeIntent(null /* preferredPackage */); .getSecondaryHomeIntent(null /* preferredPackage */); final ActivityInfo aInfoSecondary = new ActivityInfo(); final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false); aInfoSecondary.name = "fakeSecondaryHomeActivity"; aInfoSecondary.applicationInfo = new ApplicationInfo(); aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage"; doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) .resolveSecondaryHomeActivity(anyInt(), anyInt()); .resolveSecondaryHomeActivity(anyInt(), anyInt()); } } private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) { final ActivityInfo aInfo = new ActivityInfo(); aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity"; aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage"; return aInfo; } } } Loading
core/res/res/values/config.xml +6 −6 Original line number Original line Diff line number Diff line Loading @@ -3959,14 +3959,14 @@ <!-- Component name for the default module metadata provider on this device --> <!-- Component name for the default module metadata provider on this device --> <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string> <string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string> <!-- This is the default launcher component to use on secondary displays that support system <!-- This is the default launcher package with an activity to use on secondary displays that decorations. support system decorations. This launcher activity must support multiple instances and have corresponding launch mode This launcher package must have an activity that supports multiple instances and has set in AndroidManifest. corresponding launch mode set in AndroidManifest. {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> {@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} --> <string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string> <string name="config_secondaryHomePackage" translatable="false">com.android.launcher3</string> <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is <!-- Force secondary home launcher specified in config_secondaryHomePackage always. If this is not set, secondary home launcher can be replaced by user. --> not set, secondary home launcher can be replaced by user. --> <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool> <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool> Loading
core/res/res/values/symbols.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -3678,7 +3678,7 @@ <java-symbol type="string" name="config_defaultModuleMetadataProvider" /> <java-symbol type="string" name="config_defaultModuleMetadataProvider" /> <!-- For Secondary Launcher --> <!-- For Secondary Launcher --> <java-symbol type="string" name="config_secondaryHomeComponent" /> <java-symbol type="string" name="config_secondaryHomePackage" /> <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" /> <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" /> <java-symbol type="string" name="battery_saver_notification_channel_name" /> <java-symbol type="string" name="battery_saver_notification_channel_name" /> Loading
services/core/java/com/android/server/wm/ActivityTaskManagerService.java +6 −6 Original line number Original line Diff line number Diff line Loading @@ -5868,8 +5868,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home * Return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} to resolve secondary home * activities. * activities. * * * @param preferredPackage Specify a preferred package name, otherwise use secondary home * @param preferredPackage Specify a preferred package name, otherwise use the package name * component defined in config_secondaryHomeComponent. * defined in config_secondaryHomePackage. * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} * @return the intent set with {@link Intent#CATEGORY_SECONDARY_HOME} */ */ Intent getSecondaryHomeIntent(String preferredPackage) { Intent getSecondaryHomeIntent(String preferredPackage) { Loading @@ -5877,10 +5877,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean( final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); if (preferredPackage == null || useSystemProvidedLauncher) { if (preferredPackage == null || useSystemProvidedLauncher) { // Using the component stored in config if no package name or forced. // Using the package name stored in config if no preferred package name or forced. final String secondaryHomeComponent = mContext.getResources().getString( final String secondaryHomePackage = mContext.getResources().getString( com.android.internal.R.string.config_secondaryHomeComponent); com.android.internal.R.string.config_secondaryHomePackage); intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent)); intent.setPackage(secondaryHomePackage); } else { } else { intent.setPackage(preferredPackage); intent.setPackage(preferredPackage); } } Loading
services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +98 −129 Original line number Original line Diff line number Diff line Loading @@ -52,7 +52,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.refEq; import static org.mockito.ArgumentMatchers.refEq; import android.app.ActivityOptions; import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Intent; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo; Loading Loading @@ -517,7 +516,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ */ @Test @Test public void testStartHomeOnAllDisplays() { public void testStartHomeOnAllDisplays() { mockResolveHomeActivity(); mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); mockResolveSecondaryHomeActivity(); mockResolveSecondaryHomeActivity(); // Create secondary displays. // Create secondary displays. Loading Loading @@ -644,30 +643,26 @@ public class RootActivityContainerTests extends ActivityTestsBase { } } /** /** * Tests that secondary home should be selected if default home not set. * Tests that secondary home should be selected if primary home not set. */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSet() { public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSet() { final Intent defaultHomeIntent = mService.getHomeIntent(); // Setup: primary home not set. final ActivityInfo aInfoDefault = new ActivityInfo(); final Intent primaryHomeIntent = mService.getHomeIntent(); aInfoDefault.name = ResolverActivity.class.getName(); final ActivityInfo aInfoPrimary = new ActivityInfo(); doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), aInfoPrimary.name = ResolverActivity.class.getName(); refEq(defaultHomeIntent)); doReturn(aInfoPrimary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(primaryHomeIntent)); final String secondaryHomeComponent = mService.mContext.getResources().getString( // Setup: set secondary home. com.android.internal.R.string.config_secondaryHomeComponent); mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); // Run the test. final ActivityInfo aInfoSecondary = new ActivityInfo(); aInfoSecondary.name = comp.getClassName(); doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(secondaryHomeIntent)); // Should fallback to secondary home if default home not set. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); assertEquals(comp.getClassName(), resolvedInfo.first.name); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); assertEquals(aInfoSecondary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); } } /** /** Loading @@ -675,103 +670,60 @@ public class RootActivityContainerTests extends ActivityTestsBase { * config_useSystemProvidedLauncherForSecondary. * config_useSystemProvidedLauncherForSecondary. */ */ @Test @Test public void testResolveSecondaryHomeActivityForced() throws Exception { public void testResolveSecondaryHomeActivityForced() { Resources resources = mContext.getResources(); // SetUp: set primary home. spyOn(resources); mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); try { // SetUp: set secondary home and force it. // setUp: set secondary launcher and force it. mockResolveHomeActivity(false /* primaryHome */, true /* forceSystemProvided */); final String defaultSecondaryHome = final Intent secondaryHomeIntent = "com.android.test/com.android.test.TestDefaultSecondaryHome"; mService.getSecondaryHomeIntent(null /* preferredPackage */); final ComponentName secondaryComp = ComponentName.unflattenFromString( final List<ResolveInfo> resolutions = new ArrayList<>(); defaultSecondaryHome); final ResolveInfo resolveInfo = new ResolveInfo(); doReturn(defaultSecondaryHome).when(resources).getString( final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); com.android.internal.R.string.config_secondaryHomeComponent); resolveInfo.activityInfo = aInfoSecondary; doReturn(true).when(resources).getBoolean( resolutions.add(resolveInfo); com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); assertEquals(secondaryComp, secondaryHomeIntent.getComponent()); final ActivityInfo aInfoSecondary = new ActivityInfo(); aInfoSecondary.name = secondaryComp.getClassName(); aInfoSecondary.applicationInfo = new ApplicationInfo(); aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName(); doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(secondaryHomeIntent)); final Intent homeIntent = mService.getHomeIntent(); final ActivityInfo aInfoDefault = new ActivityInfo(); aInfoDefault.name = "fakeHomeActivity"; aInfoDefault.applicationInfo = new ApplicationInfo(); aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(homeIntent)); // Let resolveActivities call to validate both main launcher and second launcher so that // resolveActivities call does not work as enabler for secondary. final List<ResolveInfo> resolutions1 = new ArrayList<>(); final ResolveInfo resolveInfo1 = new ResolveInfo(); resolveInfo1.activityInfo = new ActivityInfo(); resolveInfo1.activityInfo.name = aInfoDefault.name; resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo; resolutions1.add(resolveInfo1); doReturn(resolutions1).when(mRootWindowContainer).resolveActivities(anyInt(), refEq(homeIntent)); final List<ResolveInfo> resolutions2 = new ArrayList<>(); final ResolveInfo resolveInfo2 = new ResolveInfo(); resolveInfo2.activityInfo = new ActivityInfo(); resolveInfo2.activityInfo.name = aInfoSecondary.name; resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo; resolutions2.add(resolveInfo2); doReturn(resolutions2).when(mRootWindowContainer).resolveActivities(anyInt(), refEq(secondaryHomeIntent)); refEq(secondaryHomeIntent)); doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean()); // Run the test // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name); assertEquals(secondaryComp.getPackageName(), resolvedInfo.first.applicationInfo.packageName); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); } finally { assertEquals(aInfoSecondary.applicationInfo.packageName, // tearDown resolvedInfo.first.applicationInfo.packageName); reset(resources); } } } /** /** * Tests that secondary home should be selected if default home not support secondary displays * Tests that secondary home should be selected if primary home not support secondary displays * or there is no matched activity in the same package as selected default home. * or there is no matched activity in the same package as selected primary home. */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeNotSupportMultiDisplay() { public void testResolveSecondaryHomeActivityWhenPrimaryHomeNotSupportMultiDisplay() { mockResolveHomeActivity(); // Setup: there is no matched activity in the same package as selected primary home. mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); final List<ResolveInfo> resolutions = new ArrayList<>(); final List<ResolveInfo> resolutions = new ArrayList<>(); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); // Setup: set secondary home. mockResolveHomeActivity(false /* primaryHome */, false /* forceSystemProvided */); final String secondaryHomeComponent = mService.mContext.getResources().getString( // Run the test. com.android.internal.R.string.config_secondaryHomeComponent); final ComponentName comp = ComponentName.unflattenFromString(secondaryHomeComponent); final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null); final ActivityInfo aInfoSecondary = new ActivityInfo(); aInfoSecondary.name = comp.getClassName(); doReturn(aInfoSecondary).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(secondaryHomeIntent)); // Should fallback to secondary home if selected default home not support secondary displays // or there is no matched activity in the same package as selected default home. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false /* primaryHome*/); assertEquals(comp.getClassName(), resolvedInfo.first.name); assertEquals(aInfoSecondary.name, resolvedInfo.first.name); assertEquals(aInfoSecondary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); } } /** /** * Tests that default home activity should be selected if it already support secondary displays. * Tests that primary home activity should be selected if it already support secondary displays. */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenDefaultHomeSupportMultiDisplay() { public void testResolveSecondaryHomeActivityWhenPrimaryHomeSupportMultiDisplay() { final ActivityInfo aInfoDefault = mockResolveHomeActivity(); // SetUp: set primary home. mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); // SetUp: put primary home info on 2nd item final List<ResolveInfo> resolutions = new ArrayList<>(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); final ResolveInfo infoFake1 = new ResolveInfo(); infoFake1.activityInfo = new ActivityInfo(); infoFake1.activityInfo = new ActivityInfo(); Loading @@ -779,7 +731,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); infoFake1.activityInfo.applicationInfo = new ApplicationInfo(); infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; infoFake1.activityInfo.applicationInfo.packageName = "fakePackage1"; final ResolveInfo infoFake2 = new ResolveInfo(); final ResolveInfo infoFake2 = new ResolveInfo(); infoFake2.activityInfo = aInfoDefault; final ActivityInfo aInfoPrimary = getFakeHomeActivityInfo(true /* primaryHome */); infoFake2.activityInfo = aInfoPrimary; resolutions.add(infoFake1); resolutions.add(infoFake1); resolutions.add(infoFake2); resolutions.add(infoFake2); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); doReturn(resolutions).when(mRootWindowContainer).resolveActivities(anyInt(), any()); Loading @@ -787,13 +740,12 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean()); // Use default home activity if it support secondary displays. // Run the test. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); assertEquals(aInfoPrimary.name, resolvedInfo.first.name); assertEquals(aInfoDefault.applicationInfo.packageName, assertEquals(aInfoPrimary.applicationInfo.packageName, resolvedInfo.first.applicationInfo.packageName); resolvedInfo.first.applicationInfo.packageName); assertEquals(aInfoDefault.name, resolvedInfo.first.name); } } /** /** Loading @@ -801,8 +753,9 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ */ @Test @Test public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { public void testResolveSecondaryHomeActivityWhenOtherActivitySupportMultiDisplay() { mockResolveHomeActivity(); // SetUp: set primary home. mockResolveHomeActivity(true /* primaryHome */, false /* forceSystemProvided */); // Setup: prepare two eligible activity info. final List<ResolveInfo> resolutions = new ArrayList<>(); final List<ResolveInfo> resolutions = new ArrayList<>(); final ResolveInfo infoFake1 = new ResolveInfo(); final ResolveInfo infoFake1 = new ResolveInfo(); infoFake1.activityInfo = new ActivityInfo(); infoFake1.activityInfo = new ActivityInfo(); Loading @@ -821,7 +774,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( doReturn(true).when(mRootWindowContainer).canStartHomeOnDisplay( any(), anyInt(), anyBoolean()); any(), anyInt(), anyBoolean()); // Use the first one of matched activities in the same package as selected default home. // Use the first one of matched activities in the same package as selected primary home. final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer final Pair<ActivityInfo, Intent> resolvedInfo = mRootWindowContainer .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */); Loading Loading @@ -884,32 +837,48 @@ public class RootActivityContainerTests extends ActivityTestsBase { /** /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases (the original implementation will resolve from the real package manager). * info for test cases. * * @param primaryHome Indicate to use primary home intent as parameter, otherwise, use * secondary home intent. * @param forceSystemProvided Indicate to force using system provided home activity. */ */ private ActivityInfo mockResolveHomeActivity() { private void mockResolveHomeActivity(boolean primaryHome, boolean forceSystemProvided) { final Intent homeIntent = mService.getHomeIntent(); ActivityInfo targetActivityInfo = getFakeHomeActivityInfo(primaryHome); final ActivityInfo aInfoDefault = new ActivityInfo(); Intent targetIntent; aInfoDefault.name = "fakeHomeActivity"; if (primaryHome) { aInfoDefault.applicationInfo = new ApplicationInfo(); targetIntent = mService.getHomeIntent(); aInfoDefault.applicationInfo.packageName = "fakeHomePackage"; } else { doReturn(aInfoDefault).when(mRootWindowContainer).resolveHomeActivity(anyInt(), Resources resources = mContext.getResources(); refEq(homeIntent)); spyOn(resources); return aInfoDefault; doReturn(targetActivityInfo.applicationInfo.packageName).when(resources).getString( com.android.internal.R.string.config_secondaryHomePackage); doReturn(forceSystemProvided).when(resources).getBoolean( com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary); targetIntent = mService.getSecondaryHomeIntent(null /* preferredPackage */); } doReturn(targetActivityInfo).when(mRootWindowContainer).resolveHomeActivity(anyInt(), refEq(targetIntent)); } } /** /** * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent * Mock {@link RootWindowContainer#resolveSecondaryHomeActivity} for returning consistent * activity info for test cases (the original implementation will resolve from the real package * activity info for test cases. * manager). */ */ private void mockResolveSecondaryHomeActivity() { private void mockResolveSecondaryHomeActivity() { final Intent secondaryHomeIntent = mService final Intent secondaryHomeIntent = mService .getSecondaryHomeIntent(null /* preferredPackage */); .getSecondaryHomeIntent(null /* preferredPackage */); final ActivityInfo aInfoSecondary = new ActivityInfo(); final ActivityInfo aInfoSecondary = getFakeHomeActivityInfo(false); aInfoSecondary.name = "fakeSecondaryHomeActivity"; aInfoSecondary.applicationInfo = new ApplicationInfo(); aInfoSecondary.applicationInfo.packageName = "fakeSecondaryHomePackage"; doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) doReturn(Pair.create(aInfoSecondary, secondaryHomeIntent)).when(mRootWindowContainer) .resolveSecondaryHomeActivity(anyInt(), anyInt()); .resolveSecondaryHomeActivity(anyInt(), anyInt()); } } private ActivityInfo getFakeHomeActivityInfo(boolean primaryHome) { final ActivityInfo aInfo = new ActivityInfo(); aInfo.name = primaryHome ? "fakeHomeActivity" : "fakeSecondaryHomeActivity"; aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = primaryHome ? "fakeHomePackage" : "fakeSecondaryHomePackage"; return aInfo; } } }