1/* 2 * Copyright (C) 2014 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 17package com.android.server.am; 18 19import android.app.AlertDialog; 20import android.content.Context; 21import android.content.pm.UserInfo; 22import android.content.res.Resources; 23import android.os.Handler; 24import android.os.Message; 25import android.os.UserHandle; 26import android.os.UserManager; 27import android.view.LayoutInflater; 28import android.view.View; 29import android.view.ViewTreeObserver; 30import android.view.WindowManager; 31import android.widget.TextView; 32 33import com.android.internal.R; 34import com.android.internal.annotations.GuardedBy; 35 36/** 37 * Dialog to show when a user switch it about to happen. The intent is to snapshot the screen 38 * immediately after the dialog shows so that the user is informed that something is happening 39 * in the background rather than just freeze the screen and not know if the user-switch affordance 40 * was being handled. 41 */ 42class UserSwitchingDialog extends AlertDialog 43 implements ViewTreeObserver.OnWindowShownListener { 44 private static final String TAG = "ActivityManagerUserSwitchingDialog"; 45 46 // Time to wait for the onWindowShown() callback before continuing the user switch 47 private static final int WINDOW_SHOWN_TIMEOUT_MS = 3000; 48 49 private final ActivityManagerService mService; 50 private final int mUserId; 51 private static final int MSG_START_USER = 1; 52 @GuardedBy("this") 53 private boolean mStartedUser; 54 final protected UserInfo mOldUser; 55 final protected UserInfo mNewUser; 56 final private String mSwitchingFromSystemUserMessage; 57 final private String mSwitchingToSystemUserMessage; 58 final protected Context mContext; 59 60 public UserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser, 61 UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage, 62 String switchingToSystemUserMessage) { 63 super(context); 64 65 mContext = context; 66 mService = service; 67 mUserId = newUser.id; 68 mOldUser = oldUser; 69 mNewUser = newUser; 70 mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage; 71 mSwitchingToSystemUserMessage = switchingToSystemUserMessage; 72 73 inflateContent(); 74 75 if (aboveSystem) { 76 getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); 77 } 78 79 WindowManager.LayoutParams attrs = getWindow().getAttributes(); 80 attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR | 81 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; 82 getWindow().setAttributes(attrs); 83 } 84 85 void inflateContent() { 86 // Set up the dialog contents 87 setCancelable(false); 88 Resources res = getContext().getResources(); 89 // Custom view due to alignment and font size requirements 90 View view = LayoutInflater.from(getContext()).inflate(R.layout.user_switching_dialog, 91 null); 92 93 String viewMessage = null; 94 if (UserManager.isSplitSystemUser() && mNewUser.id == UserHandle.USER_SYSTEM) { 95 viewMessage = res.getString(R.string.user_logging_out_message, mOldUser.name); 96 } else if (UserManager.isDeviceInDemoMode(mContext)) { 97 if (mOldUser.isDemo()) { 98 viewMessage = res.getString(R.string.demo_restarting_message); 99 } else { 100 viewMessage = res.getString(R.string.demo_starting_message); 101 } 102 } else { 103 if (mOldUser.id == UserHandle.USER_SYSTEM) { 104 viewMessage = mSwitchingFromSystemUserMessage; 105 } else if (mNewUser.id == UserHandle.USER_SYSTEM) { 106 viewMessage = mSwitchingToSystemUserMessage; 107 } 108 109 // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null, fallback 110 // to system message. 111 if (viewMessage == null) { 112 viewMessage = res.getString(R.string.user_switching_message, mNewUser.name); 113 } 114 } 115 ((TextView) view.findViewById(R.id.message)).setText(viewMessage); 116 setView(view); 117 } 118 119 @Override 120 public void show() { 121 // Slog.v(TAG, "show called"); 122 super.show(); 123 final View decorView = getWindow().getDecorView(); 124 if (decorView != null) { 125 decorView.getViewTreeObserver().addOnWindowShownListener(this); 126 } 127 // Add a timeout as a safeguard, in case a race in screen on/off causes the window 128 // callback to never come. 129 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_USER), 130 WINDOW_SHOWN_TIMEOUT_MS); 131 } 132 133 @Override 134 public void onWindowShown() { 135 // Slog.v(TAG, "onWindowShown called"); 136 startUser(); 137 } 138 139 void startUser() { 140 synchronized (this) { 141 if (!mStartedUser) { 142 mService.mUserController.startUserInForeground(mUserId); 143 dismiss(); 144 mStartedUser = true; 145 final View decorView = getWindow().getDecorView(); 146 if (decorView != null) { 147 decorView.getViewTreeObserver().removeOnWindowShownListener(this); 148 } 149 mHandler.removeMessages(MSG_START_USER); 150 } 151 } 152 } 153 154 private final Handler mHandler = new Handler() { 155 @Override 156 public void handleMessage(Message msg) { 157 switch (msg.what) { 158 case MSG_START_USER: 159 startUser(); 160 break; 161 } 162 } 163 }; 164} 165