1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.app.AlertDialog; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.UserInfo; 28 import android.content.res.Resources; 29 import android.os.UserHandle; 30 31 import com.android.internal.logging.UiEventLogger; 32 import com.android.systemui.broadcast.BroadcastDispatcher; 33 import com.android.systemui.dagger.qualifiers.Main; 34 import com.android.systemui.qs.QSUserSwitcherEvent; 35 import com.android.systemui.settings.UserTracker; 36 import com.android.systemui.statusbar.phone.SystemUIDialog; 37 import com.android.systemui.statusbar.policy.UserSwitcherController; 38 39 import dagger.assisted.Assisted; 40 import dagger.assisted.AssistedFactory; 41 import dagger.assisted.AssistedInject; 42 43 import javax.inject.Inject; 44 45 /** 46 * Manages handling of guest session persistent notification 47 * and actions to reset guest or exit guest session 48 */ 49 public final class GuestResetOrExitSessionReceiver extends BroadcastReceiver { 50 51 private static final String TAG = GuestResetOrExitSessionReceiver.class.getSimpleName(); 52 53 /** 54 * Broadcast sent to the system when guest user needs to be reset. 55 * This is only sent to registered receivers, not manifest receivers. 56 * 57 * @hide 58 */ 59 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 60 public static final String ACTION_GUEST_RESET = "android.intent.action.GUEST_RESET"; 61 62 /** 63 * Broadcast sent to the system when guest user needs to exit. 64 * This is only sent to registered receivers, not manifest receivers. 65 * 66 * @hide 67 */ 68 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 69 public static final String ACTION_GUEST_EXIT = "android.intent.action.GUEST_EXIT"; 70 71 public AlertDialog mExitSessionDialog; 72 public AlertDialog mResetSessionDialog; 73 private final UserTracker mUserTracker; 74 private final BroadcastDispatcher mBroadcastDispatcher; 75 private final ResetSessionDialogFactory mResetSessionDialogFactory; 76 private final ExitSessionDialogFactory mExitSessionDialogFactory; 77 78 @Inject GuestResetOrExitSessionReceiver(UserTracker userTracker, BroadcastDispatcher broadcastDispatcher, ResetSessionDialogFactory resetSessionDialogFactory, ExitSessionDialogFactory exitSessionDialogFactory)79 public GuestResetOrExitSessionReceiver(UserTracker userTracker, 80 BroadcastDispatcher broadcastDispatcher, 81 ResetSessionDialogFactory resetSessionDialogFactory, 82 ExitSessionDialogFactory exitSessionDialogFactory) { 83 mUserTracker = userTracker; 84 mBroadcastDispatcher = broadcastDispatcher; 85 mResetSessionDialogFactory = resetSessionDialogFactory; 86 mExitSessionDialogFactory = exitSessionDialogFactory; 87 } 88 89 /** 90 * Register this receiver with the {@link BroadcastDispatcher} 91 */ register()92 public void register() { 93 IntentFilter intentFilter = new IntentFilter(); 94 intentFilter.addAction(ACTION_GUEST_RESET); 95 intentFilter.addAction(ACTION_GUEST_EXIT); 96 mBroadcastDispatcher.registerReceiver(this, intentFilter, null /* handler */, 97 UserHandle.SYSTEM); 98 } 99 100 @Override onReceive(Context context, Intent intent)101 public void onReceive(Context context, Intent intent) { 102 String action = intent.getAction(); 103 104 cancelResetDialog(); 105 cancelExitDialog(); 106 107 UserInfo currentUser = mUserTracker.getUserInfo(); 108 if (!currentUser.isGuest()) { 109 return; 110 } 111 112 if (ACTION_GUEST_RESET.equals(action)) { 113 mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id); 114 mResetSessionDialog.show(); 115 } else if (ACTION_GUEST_EXIT.equals(action)) { 116 mExitSessionDialog = mExitSessionDialogFactory.create( 117 currentUser.isEphemeral(), currentUser.id); 118 mExitSessionDialog.show(); 119 } 120 } 121 cancelResetDialog()122 private void cancelResetDialog() { 123 if (mResetSessionDialog != null && mResetSessionDialog.isShowing()) { 124 mResetSessionDialog.cancel(); 125 mResetSessionDialog = null; 126 } 127 } 128 cancelExitDialog()129 private void cancelExitDialog() { 130 if (mExitSessionDialog != null && mExitSessionDialog.isShowing()) { 131 mExitSessionDialog.cancel(); 132 mExitSessionDialog = null; 133 } 134 } 135 136 /** 137 * Factory class to create guest reset dialog instance 138 * 139 * Dialog shown when asking for confirmation before 140 * reset and restart of guest user. 141 */ 142 public static final class ResetSessionDialogFactory { 143 private final SystemUIDialog.Factory mDialogFactory; 144 private final Resources mResources; 145 private final ResetSessionDialogClickListener.Factory mClickListenerFactory; 146 147 @Inject ResetSessionDialogFactory( SystemUIDialog.Factory dialogFactory, @Main Resources resources, ResetSessionDialogClickListener.Factory clickListenerFactory)148 public ResetSessionDialogFactory( 149 SystemUIDialog.Factory dialogFactory, 150 @Main Resources resources, 151 ResetSessionDialogClickListener.Factory clickListenerFactory) { 152 mDialogFactory = dialogFactory; 153 mResources = resources; 154 mClickListenerFactory = clickListenerFactory; 155 } 156 157 /** Create a guest reset dialog instance */ create(int userId)158 public AlertDialog create(int userId) { 159 SystemUIDialog dialog = mDialogFactory.create(); 160 ResetSessionDialogClickListener listener = mClickListenerFactory.create( 161 userId, dialog); 162 dialog.setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title); 163 dialog.setMessage(mResources.getString( 164 com.android.settingslib.R.string.guest_reset_and_restart_dialog_message)); 165 dialog.setButton( 166 DialogInterface.BUTTON_NEUTRAL, 167 mResources.getString(android.R.string.cancel), 168 listener); 169 dialog.setButton(DialogInterface.BUTTON_POSITIVE, 170 mResources.getString( 171 com.android.settingslib.R.string.guest_reset_guest_confirm_button), 172 listener); 173 dialog.setCanceledOnTouchOutside(false); 174 return dialog; 175 } 176 } 177 178 public static class ResetSessionDialogClickListener implements DialogInterface.OnClickListener { 179 private final UserSwitcherController mUserSwitcherController; 180 private final UiEventLogger mUiEventLogger; 181 private final int mUserId; 182 private final DialogInterface mDialog; 183 184 @AssistedFactory 185 public interface Factory { create(int userId, DialogInterface dialog)186 ResetSessionDialogClickListener create(int userId, DialogInterface dialog); 187 } 188 189 @AssistedInject ResetSessionDialogClickListener( UserSwitcherController userSwitcherController, UiEventLogger uiEventLogger, @Assisted int userId, @Assisted DialogInterface dialog )190 public ResetSessionDialogClickListener( 191 UserSwitcherController userSwitcherController, 192 UiEventLogger uiEventLogger, 193 @Assisted int userId, 194 @Assisted DialogInterface dialog 195 ) { 196 mUserSwitcherController = userSwitcherController; 197 mUiEventLogger = uiEventLogger; 198 mUserId = userId; 199 mDialog = dialog; 200 } 201 202 @Override onClick(DialogInterface dialog, int which)203 public void onClick(DialogInterface dialog, int which) { 204 if (which == DialogInterface.BUTTON_POSITIVE) { 205 mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); 206 mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL); 207 } else if (which == DialogInterface.BUTTON_NEUTRAL) { 208 mDialog.cancel(); 209 } 210 } 211 } 212 213 /** 214 * Dialog shown when asking for confirmation before 215 * exit of guest user. 216 */ 217 public static final class ExitSessionDialogFactory { 218 private final SystemUIDialog.Factory mDialogFactory; 219 private final ExitSessionDialogClickListener.Factory mClickListenerFactory; 220 private final Resources mResources; 221 222 @Inject ExitSessionDialogFactory( SystemUIDialog.Factory dialogFactory, ExitSessionDialogClickListener.Factory clickListenerFactory, @Main Resources resources)223 public ExitSessionDialogFactory( 224 SystemUIDialog.Factory dialogFactory, 225 ExitSessionDialogClickListener.Factory clickListenerFactory, 226 @Main Resources resources) { 227 mDialogFactory = dialogFactory; 228 mClickListenerFactory = clickListenerFactory; 229 mResources = resources; 230 } 231 create(boolean isEphemeral, int userId)232 public AlertDialog create(boolean isEphemeral, int userId) { 233 SystemUIDialog dialog = mDialogFactory.create(); 234 ExitSessionDialogClickListener clickListener = mClickListenerFactory.create( 235 isEphemeral, userId, dialog); 236 if (isEphemeral) { 237 dialog.setTitle(mResources.getString( 238 com.android.settingslib.R.string.guest_exit_dialog_title)); 239 dialog.setMessage(mResources.getString( 240 com.android.settingslib.R.string.guest_exit_dialog_message)); 241 dialog.setButton( 242 DialogInterface.BUTTON_NEUTRAL, 243 mResources.getString(android.R.string.cancel), 244 clickListener); 245 dialog.setButton( 246 DialogInterface.BUTTON_POSITIVE, 247 mResources.getString( 248 com.android.settingslib.R.string.guest_exit_dialog_button), 249 clickListener); 250 } else { 251 dialog.setTitle(mResources.getString( 252 com.android.settingslib 253 .R.string.guest_exit_dialog_title_non_ephemeral)); 254 dialog.setMessage(mResources.getString( 255 com.android.settingslib 256 .R.string.guest_exit_dialog_message_non_ephemeral)); 257 dialog.setButton( 258 DialogInterface.BUTTON_NEUTRAL, 259 mResources.getString(android.R.string.cancel), 260 clickListener); 261 dialog.setButton( 262 DialogInterface.BUTTON_NEGATIVE, 263 mResources.getString( 264 com.android.settingslib.R.string.guest_exit_clear_data_button), 265 clickListener); 266 dialog.setButton( 267 DialogInterface.BUTTON_POSITIVE, 268 mResources.getString( 269 com.android.settingslib.R.string.guest_exit_save_data_button), 270 clickListener); 271 } 272 dialog.setCanceledOnTouchOutside(false); 273 274 return dialog; 275 } 276 277 } 278 279 public static class ExitSessionDialogClickListener implements DialogInterface.OnClickListener { 280 private final UserSwitcherController mUserSwitcherController; 281 private final boolean mIsEphemeral; 282 private final int mUserId; 283 private final DialogInterface mDialog; 284 285 @AssistedFactory 286 public interface Factory { create( boolean isEphemeral, int userId, DialogInterface dialog)287 ExitSessionDialogClickListener create( 288 boolean isEphemeral, 289 int userId, 290 DialogInterface dialog); 291 } 292 293 @AssistedInject ExitSessionDialogClickListener( UserSwitcherController userSwitcherController, @Assisted boolean isEphemeral, @Assisted int userId, @Assisted DialogInterface dialog )294 public ExitSessionDialogClickListener( 295 UserSwitcherController userSwitcherController, 296 @Assisted boolean isEphemeral, 297 @Assisted int userId, 298 @Assisted DialogInterface dialog 299 ) { 300 mUserSwitcherController = userSwitcherController; 301 mIsEphemeral = isEphemeral; 302 mUserId = userId; 303 mDialog = dialog; 304 } 305 306 @Override onClick(DialogInterface dialog, int which)307 public void onClick(DialogInterface dialog, int which) { 308 if (mIsEphemeral) { 309 if (which == DialogInterface.BUTTON_POSITIVE) { 310 // Ephemeral guest: exit guest, guest is removed by the system 311 // on exit, since its marked ephemeral 312 mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false); 313 } else if (which == DialogInterface.BUTTON_NEUTRAL) { 314 // Cancel clicked, do nothing 315 mDialog.cancel(); 316 } 317 } else { 318 if (which == DialogInterface.BUTTON_POSITIVE) { 319 // Non-ephemeral guest: exit guest, guest is not removed by the system 320 // on exit, since its marked non-ephemeral 321 mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false); 322 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 323 // Non-ephemeral guest: remove guest and then exit 324 mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true); 325 } else if (which == DialogInterface.BUTTON_NEUTRAL) { 326 // Cancel clicked, do nothing 327 mDialog.cancel(); 328 } 329 } 330 } 331 } 332 } 333