diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java index 300cbdbb2fb..2ca22d1d478 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java @@ -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; @@ -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; @@ -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; } @@ -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; @@ -162,6 +168,7 @@ public class PrivateSpaceMaintainer { for (UserInfo user : users) { if (user.isPrivateProfile()) { mUserHandle = user.getUserHandle(); + registerBroadcastReceiver(); return true; } } @@ -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); } @@ -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 appTasks = mActivityManager.getAppTasks(); + for (var appTask : appTasks) { + if (!appTask.getTaskInfo().isVisible()) { + appTask.finishAndRemoveTask(); + } + } + } } diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java index 6f3cb75820f..f75e76f31b3 100644 --- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java +++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java @@ -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) @@ -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.