Loading src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +101 −6 Original line number Diff line number Diff line Loading @@ -23,20 +23,21 @@ import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEV import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import android.app.ActivityManager; import android.app.IActivityManager; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; import android.os.Flags; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.ArraySet; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; Loading @@ -54,9 +55,12 @@ public class PrivateSpaceMaintainer { private final Context mContext; private final UserManager mUserManager; private final ActivityManager mActivityManager; @GuardedBy("this") private UserHandle mUserHandle; private final KeyguardManager mKeyguardManager; /** This variable should be accessed via {@link #getBroadcastReceiver()} only. */ @Nullable private ProfileAvailabilityBroadcastReceiver mProfileAvailabilityBroadcastReceiver; /** This is the default value for the hide private space entry point settings. */ public static final int HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL = 0; Loading Loading @@ -106,12 +110,13 @@ public class PrivateSpaceMaintainer { return false; } IActivityManager am = ActivityManager.getService(); registerBroadcastReceiver(); try { //TODO(b/313926659): To check and handle failure of startProfile am.startProfile(mUserHandle.getIdentifier()); } catch (RemoteException e) { Log.e(TAG, "Failed to start private profile"); mActivityManager.startProfile(mUserHandle); } catch (IllegalArgumentException e) { Log.e(TAG, "Unexpected that " + mUserHandle.getIdentifier() + " is not a profile"); return false; } Loading @@ -136,6 +141,7 @@ public class PrivateSpaceMaintainer { Log.i(TAG, "Deleting Private space with id: " + mUserHandle.getIdentifier()); if (mUserManager.removeUser(mUserHandle)) { Log.i(TAG, "Private space deleted"); unregisterBroadcastReceiver(); mUserHandle = null; return ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE; Loading @@ -162,6 +168,7 @@ public class PrivateSpaceMaintainer { for (UserInfo user : users) { if (user.isPrivateProfile()) { mUserHandle = user.getUserHandle(); registerBroadcastReceiver(); return true; } } Loading Loading @@ -215,6 +222,7 @@ public class PrivateSpaceMaintainer { mContext = context.getApplicationContext(); mUserManager = mContext.getSystemService(UserManager.class); mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mActivityManager = mContext.getSystemService(ActivityManager.class); } Loading Loading @@ -338,4 +346,91 @@ public class PrivateSpaceMaintainer { && android.multiuser.Flags.supportAutolockForPrivateSpace() && android.multiuser.Flags.enablePrivateSpaceFeatures(); } /** {@link BroadcastReceiver} which handles the private profile's availability related * broadcasts. */ private final class ProfileAvailabilityBroadcastReceiver extends BroadcastReceiver { void register() { Log.d(TAG, "Registering the receiver"); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE); mContext.registerReceiver(/* receiver= */ this, filter, Context.RECEIVER_NOT_EXPORTED); } void unregister() { Log.d(TAG, "Unregistering the receiver"); mContext.unregisterReceiver(/* receiver= */ this); } @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); if (!userHandle.equals(getPrivateProfileHandle())) { Log.d(TAG, "Ignoring intent for non-private profile with user id " + userHandle.getIdentifier()); return; } Log.i(TAG, "Removing all Settings tasks."); removeSettingsAllTasks(); } } private synchronized void registerBroadcastReceiver() { if (!android.os.Flags.allowPrivateProfile() || !android.multiuser.Flags.enablePrivateSpaceFeatures()) { return; } var broadcastReceiver = getBroadcastReceiver(); if (broadcastReceiver == null) { return; } broadcastReceiver.register(); } private synchronized void unregisterBroadcastReceiver() { if (!android.os.Flags.allowPrivateProfile() || !android.multiuser.Flags.enablePrivateSpaceFeatures()) { return; } if (mProfileAvailabilityBroadcastReceiver == null) { Log.w(TAG, "Requested to unregister when there is no receiver."); return; } mProfileAvailabilityBroadcastReceiver.unregister(); mProfileAvailabilityBroadcastReceiver = null; } /** Always use this getter to access {@link #mProfileAvailabilityBroadcastReceiver}. */ @VisibleForTesting @Nullable synchronized ProfileAvailabilityBroadcastReceiver getBroadcastReceiver() { if (!android.os.Flags.allowPrivateProfile() || !android.multiuser.Flags.enablePrivateSpaceFeatures()) { return null; } if (!doesPrivateSpaceExist()) { Log.e(TAG, "Cannot return a broadcast receiver when private space doesn't exist"); return null; } if (mProfileAvailabilityBroadcastReceiver == null) { mProfileAvailabilityBroadcastReceiver = new ProfileAvailabilityBroadcastReceiver(); } return mProfileAvailabilityBroadcastReceiver; } /** This is purely for testing purpose only, and should not be used elsewhere. */ @VisibleForTesting synchronized void resetBroadcastReceiver() { mProfileAvailabilityBroadcastReceiver = null; } private void removeSettingsAllTasks() { List<ActivityManager.AppTask> appTasks = mActivityManager.getAppTasks(); for (var appTask : appTasks) { if (!appTask.getTaskInfo().isVisible()) { appTask.finishAndRemoveTask(); } } } } tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java +25 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ public class PrivateSpaceMaintainerTest { android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); privateSpaceMaintainer.deletePrivateSpace(); ErrorDeletingPrivateSpace errorDeletingPrivateSpace = privateSpaceMaintainer.deletePrivateSpace(); assertThat(errorDeletingPrivateSpace) Loading Loading @@ -197,6 +198,30 @@ public class PrivateSpaceMaintainerTest { .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL); } @Test public void createPrivateSpace_psDoesNotExist_registersTheBroadcastReceiver() { mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); privateSpaceMaintainer.deletePrivateSpace(); privateSpaceMaintainer.createPrivateSpace(); // test that no exception is thrown, which would indicate that the receiver was registered. mContext.unregisterReceiver(privateSpaceMaintainer.getBroadcastReceiver()); privateSpaceMaintainer.resetBroadcastReceiver(); } @Test public void deletePrivateSpace_psExists_unregistersTheBroadcastReceiver() { mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); privateSpaceMaintainer.createPrivateSpace(); privateSpaceMaintainer.deletePrivateSpace(); assertThat(privateSpaceMaintainer.getBroadcastReceiver()).isNull(); } /** * Tests that {@link PrivateSpaceMaintainer#lockPrivateSpace()} when PS exists and is running * locks the private profile. Loading Loading
src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +101 −6 Original line number Diff line number Diff line Loading @@ -23,20 +23,21 @@ import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEV import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import android.app.ActivityManager; import android.app.IActivityManager; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; import android.os.Flags; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.ArraySet; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; Loading @@ -54,9 +55,12 @@ public class PrivateSpaceMaintainer { private final Context mContext; private final UserManager mUserManager; private final ActivityManager mActivityManager; @GuardedBy("this") private UserHandle mUserHandle; private final KeyguardManager mKeyguardManager; /** This variable should be accessed via {@link #getBroadcastReceiver()} only. */ @Nullable private ProfileAvailabilityBroadcastReceiver mProfileAvailabilityBroadcastReceiver; /** This is the default value for the hide private space entry point settings. */ public static final int HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL = 0; Loading Loading @@ -106,12 +110,13 @@ public class PrivateSpaceMaintainer { return false; } IActivityManager am = ActivityManager.getService(); registerBroadcastReceiver(); try { //TODO(b/313926659): To check and handle failure of startProfile am.startProfile(mUserHandle.getIdentifier()); } catch (RemoteException e) { Log.e(TAG, "Failed to start private profile"); mActivityManager.startProfile(mUserHandle); } catch (IllegalArgumentException e) { Log.e(TAG, "Unexpected that " + mUserHandle.getIdentifier() + " is not a profile"); return false; } Loading @@ -136,6 +141,7 @@ public class PrivateSpaceMaintainer { Log.i(TAG, "Deleting Private space with id: " + mUserHandle.getIdentifier()); if (mUserManager.removeUser(mUserHandle)) { Log.i(TAG, "Private space deleted"); unregisterBroadcastReceiver(); mUserHandle = null; return ErrorDeletingPrivateSpace.DELETE_PS_ERROR_NONE; Loading @@ -162,6 +168,7 @@ public class PrivateSpaceMaintainer { for (UserInfo user : users) { if (user.isPrivateProfile()) { mUserHandle = user.getUserHandle(); registerBroadcastReceiver(); return true; } } Loading Loading @@ -215,6 +222,7 @@ public class PrivateSpaceMaintainer { mContext = context.getApplicationContext(); mUserManager = mContext.getSystemService(UserManager.class); mKeyguardManager = mContext.getSystemService(KeyguardManager.class); mActivityManager = mContext.getSystemService(ActivityManager.class); } Loading Loading @@ -338,4 +346,91 @@ public class PrivateSpaceMaintainer { && android.multiuser.Flags.supportAutolockForPrivateSpace() && android.multiuser.Flags.enablePrivateSpaceFeatures(); } /** {@link BroadcastReceiver} which handles the private profile's availability related * broadcasts. */ private final class ProfileAvailabilityBroadcastReceiver extends BroadcastReceiver { void register() { Log.d(TAG, "Registering the receiver"); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE); mContext.registerReceiver(/* receiver= */ this, filter, Context.RECEIVER_NOT_EXPORTED); } void unregister() { Log.d(TAG, "Unregistering the receiver"); mContext.unregisterReceiver(/* receiver= */ this); } @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); if (!userHandle.equals(getPrivateProfileHandle())) { Log.d(TAG, "Ignoring intent for non-private profile with user id " + userHandle.getIdentifier()); return; } Log.i(TAG, "Removing all Settings tasks."); removeSettingsAllTasks(); } } private synchronized void registerBroadcastReceiver() { if (!android.os.Flags.allowPrivateProfile() || !android.multiuser.Flags.enablePrivateSpaceFeatures()) { return; } var broadcastReceiver = getBroadcastReceiver(); if (broadcastReceiver == null) { return; } broadcastReceiver.register(); } private synchronized void unregisterBroadcastReceiver() { if (!android.os.Flags.allowPrivateProfile() || !android.multiuser.Flags.enablePrivateSpaceFeatures()) { return; } if (mProfileAvailabilityBroadcastReceiver == null) { Log.w(TAG, "Requested to unregister when there is no receiver."); return; } mProfileAvailabilityBroadcastReceiver.unregister(); mProfileAvailabilityBroadcastReceiver = null; } /** Always use this getter to access {@link #mProfileAvailabilityBroadcastReceiver}. */ @VisibleForTesting @Nullable synchronized ProfileAvailabilityBroadcastReceiver getBroadcastReceiver() { if (!android.os.Flags.allowPrivateProfile() || !android.multiuser.Flags.enablePrivateSpaceFeatures()) { return null; } if (!doesPrivateSpaceExist()) { Log.e(TAG, "Cannot return a broadcast receiver when private space doesn't exist"); return null; } if (mProfileAvailabilityBroadcastReceiver == null) { mProfileAvailabilityBroadcastReceiver = new ProfileAvailabilityBroadcastReceiver(); } return mProfileAvailabilityBroadcastReceiver; } /** This is purely for testing purpose only, and should not be used elsewhere. */ @VisibleForTesting synchronized void resetBroadcastReceiver() { mProfileAvailabilityBroadcastReceiver = null; } private void removeSettingsAllTasks() { List<ActivityManager.AppTask> appTasks = mActivityManager.getAppTasks(); for (var appTask : appTasks) { if (!appTask.getTaskInfo().isVisible()) { appTask.finishAndRemoveTask(); } } } }
tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java +25 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ public class PrivateSpaceMaintainerTest { android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); privateSpaceMaintainer.deletePrivateSpace(); ErrorDeletingPrivateSpace errorDeletingPrivateSpace = privateSpaceMaintainer.deletePrivateSpace(); assertThat(errorDeletingPrivateSpace) Loading Loading @@ -197,6 +198,30 @@ public class PrivateSpaceMaintainerTest { .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL); } @Test public void createPrivateSpace_psDoesNotExist_registersTheBroadcastReceiver() { mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); privateSpaceMaintainer.deletePrivateSpace(); privateSpaceMaintainer.createPrivateSpace(); // test that no exception is thrown, which would indicate that the receiver was registered. mContext.unregisterReceiver(privateSpaceMaintainer.getBroadcastReceiver()); privateSpaceMaintainer.resetBroadcastReceiver(); } @Test public void deletePrivateSpace_psExists_unregistersTheBroadcastReceiver() { mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE, android.multiuser.Flags.FLAG_ENABLE_PRIVATE_SPACE_FEATURES); PrivateSpaceMaintainer privateSpaceMaintainer = PrivateSpaceMaintainer.getInstance(mContext); privateSpaceMaintainer.createPrivateSpace(); privateSpaceMaintainer.deletePrivateSpace(); assertThat(privateSpaceMaintainer.getBroadcastReceiver()).isNull(); } /** * Tests that {@link PrivateSpaceMaintainer#lockPrivateSpace()} when PS exists and is running * locks the private profile. Loading