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.os.UserHandle;
29 
30 import com.android.internal.logging.UiEventLogger;
31 import com.android.systemui.broadcast.BroadcastDispatcher;
32 import com.android.systemui.qs.QSUserSwitcherEvent;
33 import com.android.systemui.settings.UserTracker;
34 import com.android.systemui.statusbar.phone.SystemUIDialog;
35 import com.android.systemui.statusbar.policy.UserSwitcherController;
36 
37 import javax.inject.Inject;
38 
39 import dagger.assisted.Assisted;
40 import dagger.assisted.AssistedFactory;
41 import dagger.assisted.AssistedInject;
42 
43 /**
44  * Manages handling of guest session persistent notification
45  * and actions to reset guest or exit guest session
46  */
47 public final class GuestResetOrExitSessionReceiver extends BroadcastReceiver {
48 
49     private static final String TAG = GuestResetOrExitSessionReceiver.class.getSimpleName();
50 
51     /**
52      * Broadcast sent to the system when guest user needs to be reset.
53      * This is only sent to registered receivers, not manifest receivers.
54      *
55      * @hide
56      */
57     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
58     public static final String ACTION_GUEST_RESET = "android.intent.action.GUEST_RESET";
59 
60     /**
61      * Broadcast sent to the system when guest user needs to exit.
62      * This is only sent to registered receivers, not manifest receivers.
63      *
64      * @hide
65      */
66     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
67     public static final String ACTION_GUEST_EXIT = "android.intent.action.GUEST_EXIT";
68 
69     public AlertDialog mExitSessionDialog;
70     public AlertDialog mResetSessionDialog;
71     private final UserTracker mUserTracker;
72     private final BroadcastDispatcher mBroadcastDispatcher;
73     private final ResetSessionDialog.Factory mResetSessionDialogFactory;
74     private final ExitSessionDialog.Factory mExitSessionDialogFactory;
75 
76     @Inject
GuestResetOrExitSessionReceiver(UserTracker userTracker, BroadcastDispatcher broadcastDispatcher, ResetSessionDialog.Factory resetSessionDialogFactory, ExitSessionDialog.Factory exitSessionDialogFactory)77     public GuestResetOrExitSessionReceiver(UserTracker userTracker,
78             BroadcastDispatcher broadcastDispatcher,
79             ResetSessionDialog.Factory resetSessionDialogFactory,
80             ExitSessionDialog.Factory exitSessionDialogFactory) {
81         mUserTracker = userTracker;
82         mBroadcastDispatcher = broadcastDispatcher;
83         mResetSessionDialogFactory = resetSessionDialogFactory;
84         mExitSessionDialogFactory = exitSessionDialogFactory;
85     }
86 
87     /**
88      * Register this receiver with the {@link BroadcastDispatcher}
89      */
register()90     public void register() {
91         IntentFilter intentFilter = new IntentFilter();
92         intentFilter.addAction(ACTION_GUEST_RESET);
93         intentFilter.addAction(ACTION_GUEST_EXIT);
94         mBroadcastDispatcher.registerReceiver(this, intentFilter, null /* handler */,
95                                              UserHandle.SYSTEM);
96     }
97 
98     @Override
onReceive(Context context, Intent intent)99     public void onReceive(Context context, Intent intent) {
100         String action = intent.getAction();
101 
102         cancelResetDialog();
103         cancelExitDialog();
104 
105         UserInfo currentUser = mUserTracker.getUserInfo();
106         if (!currentUser.isGuest()) {
107             return;
108         }
109 
110         if (ACTION_GUEST_RESET.equals(action)) {
111             mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id);
112             mResetSessionDialog.show();
113         } else if (ACTION_GUEST_EXIT.equals(action)) {
114             mExitSessionDialog = mExitSessionDialogFactory.create(currentUser.id,
115                         currentUser.isEphemeral());
116             mExitSessionDialog.show();
117         }
118     }
119 
cancelResetDialog()120     private void cancelResetDialog() {
121         if (mResetSessionDialog != null && mResetSessionDialog.isShowing()) {
122             mResetSessionDialog.cancel();
123             mResetSessionDialog = null;
124         }
125     }
126 
cancelExitDialog()127     private void cancelExitDialog() {
128         if (mExitSessionDialog != null && mExitSessionDialog.isShowing()) {
129             mExitSessionDialog.cancel();
130             mExitSessionDialog = null;
131         }
132     }
133 
134     /**
135      * Dialog shown when asking for confirmation before
136      * reset and restart of guest user.
137      */
138     public static final class ResetSessionDialog extends SystemUIDialog implements
139             DialogInterface.OnClickListener {
140 
141         private final UserSwitcherController mUserSwitcherController;
142         private final UiEventLogger mUiEventLogger;
143         private final int mUserId;
144 
145         /** Factory class to create guest reset dialog instance */
146         @AssistedFactory
147         public interface Factory {
148             /** Create a guest reset dialog instance */
create(int userId)149             ResetSessionDialog create(int userId);
150         }
151 
152         @AssistedInject
ResetSessionDialog(Context context, UserSwitcherController userSwitcherController, UiEventLogger uiEventLogger, @Assisted int userId)153         ResetSessionDialog(Context context,
154                 UserSwitcherController userSwitcherController,
155                 UiEventLogger uiEventLogger,
156                 @Assisted int userId) {
157             super(context);
158 
159             setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title);
160             setMessage(context.getString(
161                         com.android.settingslib.R.string.guest_reset_and_restart_dialog_message));
162             setButton(DialogInterface.BUTTON_NEUTRAL,
163                     context.getString(android.R.string.cancel), this);
164             setButton(DialogInterface.BUTTON_POSITIVE,
165                     context.getString(
166                         com.android.settingslib.R.string.guest_reset_guest_confirm_button), this);
167             setCanceledOnTouchOutside(false);
168 
169             mUserSwitcherController = userSwitcherController;
170             mUiEventLogger = uiEventLogger;
171             mUserId = userId;
172         }
173 
174         @Override
onClick(DialogInterface dialog, int which)175         public void onClick(DialogInterface dialog, int which) {
176             if (which == DialogInterface.BUTTON_POSITIVE) {
177                 mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
178                 mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL);
179             } else if (which == DialogInterface.BUTTON_NEUTRAL) {
180                 cancel();
181             }
182         }
183     }
184 
185     /**
186      * Dialog shown when asking for confirmation before
187      * exit of guest user.
188      */
189     public static final class ExitSessionDialog extends SystemUIDialog implements
190             DialogInterface.OnClickListener {
191 
192         private final UserSwitcherController mUserSwitcherController;
193         private final int mUserId;
194         private boolean mIsEphemeral;
195 
196         /** Factory class to create guest exit dialog instance */
197         @AssistedFactory
198         public interface Factory {
199             /** Create a guest exit dialog instance */
create(int userId, boolean isEphemeral)200             ExitSessionDialog create(int userId, boolean isEphemeral);
201         }
202 
203         @AssistedInject
ExitSessionDialog(Context context, UserSwitcherController userSwitcherController, @Assisted int userId, @Assisted boolean isEphemeral)204         ExitSessionDialog(Context context,
205                 UserSwitcherController userSwitcherController,
206                 @Assisted int userId,
207                 @Assisted boolean isEphemeral) {
208             super(context);
209 
210             if (isEphemeral) {
211                 setTitle(context.getString(
212                             com.android.settingslib.R.string.guest_exit_dialog_title));
213                 setMessage(context.getString(
214                             com.android.settingslib.R.string.guest_exit_dialog_message));
215                 setButton(DialogInterface.BUTTON_NEUTRAL,
216                         context.getString(android.R.string.cancel), this);
217                 setButton(DialogInterface.BUTTON_POSITIVE,
218                         context.getString(
219                             com.android.settingslib.R.string.guest_exit_dialog_button), this);
220             } else {
221                 setTitle(context.getString(
222                             com.android.settingslib
223                                 .R.string.guest_exit_dialog_title_non_ephemeral));
224                 setMessage(context.getString(
225                             com.android.settingslib
226                                 .R.string.guest_exit_dialog_message_non_ephemeral));
227                 setButton(DialogInterface.BUTTON_NEUTRAL,
228                         context.getString(android.R.string.cancel), this);
229                 setButton(DialogInterface.BUTTON_NEGATIVE,
230                         context.getString(
231                             com.android.settingslib.R.string.guest_exit_clear_data_button), this);
232                 setButton(DialogInterface.BUTTON_POSITIVE,
233                         context.getString(
234                             com.android.settingslib.R.string.guest_exit_save_data_button), this);
235             }
236             setCanceledOnTouchOutside(false);
237 
238             mUserSwitcherController = userSwitcherController;
239             mUserId = userId;
240             mIsEphemeral = isEphemeral;
241         }
242 
243         @Override
onClick(DialogInterface dialog, int which)244         public void onClick(DialogInterface dialog, int which) {
245             if (mIsEphemeral) {
246                 if (which == DialogInterface.BUTTON_POSITIVE) {
247                     // Ephemeral guest: exit guest, guest is removed by the system
248                     // on exit, since its marked ephemeral
249                     mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
250                 } else if (which == DialogInterface.BUTTON_NEUTRAL) {
251                     // Cancel clicked, do nothing
252                     cancel();
253                 }
254             } else {
255                 if (which == DialogInterface.BUTTON_POSITIVE) {
256                     // Non-ephemeral guest: exit guest, guest is not removed by the system
257                     // on exit, since its marked non-ephemeral
258                     mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false);
259                 } else if (which == DialogInterface.BUTTON_NEGATIVE) {
260                     // Non-ephemeral guest: remove guest and then exit
261                     mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true);
262                 } else if (which == DialogInterface.BUTTON_NEUTRAL) {
263                     // Cancel clicked, do nothing
264                     cancel();
265                 }
266             }
267         }
268     }
269 }
270