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 17 package com.android.tv.settings.users; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.app.Activity; 22 import android.app.ActivityManagerNative; 23 import android.app.Fragment; 24 import android.content.Context; 25 import android.content.pm.IPackageManager; 26 import android.content.pm.UserInfo; 27 import android.graphics.Bitmap; 28 import android.graphics.Canvas; 29 import android.graphics.drawable.Drawable; 30 import android.os.AsyncTask; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.provider.Settings.Secure; 38 import android.util.Log; 39 40 import com.android.internal.widget.ILockSettings; 41 import com.android.internal.widget.LockPatternUtils; 42 import com.android.internal.widget.VerifyCredentialResponse; 43 import com.android.tv.settings.R; 44 import com.android.tv.settings.dialog.DialogFragment; 45 import com.android.tv.settings.dialog.DialogFragment.Action; 46 import com.android.tv.settings.dialog.PinDialogFragment; 47 48 import java.util.ArrayList; 49 50 /** 51 * Activity that allows the configuration of a user's restricted profile. 52 */ 53 public class RestrictedProfileDialogFragment extends Fragment implements Action.Listener, 54 AppLoadingTask.Listener, RestrictedProfilePinDialogFragment.Callback { 55 56 private static final String TAG = "RestrictedProfile"; 57 58 private static final String ACTION_RESTRICTED_PROFILE_SETUP_LOCKSCREEN = 59 "restricted_setup_locakscreen"; 60 private static final String ACTION_RESTRICTED_PROFILE_CREATE = "restricted_profile_create"; 61 private static final String ACTION_RESTRICTED_PROFILE_SWITCH_TO = 62 "restricted_profile_switch_to"; 63 private static final String ACTION_RESTRICTED_PROFILE_SWITCH_OUT = 64 "restricted_profile_switch_out"; 65 private static final String ACTION_RESTRICTED_PROFILE_CONFIG = "restricted_profile_config"; 66 private static final String ACTION_RESTRICTED_PROFILE_CONFIG_APPS = 67 "restricted_profile_config_apps"; 68 private static final String ACTION_RESTRICTED_PROFILE_CHANGE_PASSWORD = 69 "restricted_profile_change_password"; 70 private static final String ACTION_RESTRICTED_PROFILE_DELETE = "restricted_profile_delete"; 71 private static final String 72 ACTION_RESTRICTED_PROFILE_DELETE_CONFIRM = "restricted_profile_delete_confirm"; 73 private static final String 74 ACTION_RESTRICTED_PROFILE_DELETE_CANCEL = "restricted_profile_delete_cancel"; 75 76 private static final String STATE_PIN_MODE = "RestrictedProfileActivity.pinMode"; 77 78 private static final int PIN_MODE_NONE = 0; 79 private static final int PIN_MODE_CHOOSE_LOCKSCREEN = 1; 80 private static final int PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT = 2; 81 private static final int PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD = 3; 82 private static final int PIN_MODE_RESTRICTED_PROFILE_DELETE = 4; 83 84 private UserManager mUserManager; 85 private UserInfo mRestrictedUserInfo; 86 private DialogFragment mMainMenuDialogFragment; 87 private ILockSettings mLockSettingsService; 88 private Handler mHandler; 89 private IPackageManager mIPm; 90 private AppLoadingTask mAppLoadingTask; 91 private Action mConfigAppsAction; 92 private DialogFragment mConfigDialogFragment; 93 94 private int mPinMode; 95 96 private final boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER; 97 private final AsyncTask<Void, Void, UserInfo> mAddUserAsyncTask = 98 new AsyncTask<Void, Void, UserInfo>() { 99 @Override 100 protected UserInfo doInBackground(Void... params) { 101 UserInfo restrictedUserInfo = mUserManager.createUser( 102 RestrictedProfileDialogFragment.this.getString(R.string.user_new_profile_name), 103 UserInfo.FLAG_RESTRICTED); 104 if (restrictedUserInfo == null) { 105 Log.wtf(TAG, "Got back a null user handle!"); 106 return null; 107 } 108 int userId = restrictedUserInfo.id; 109 UserHandle user = new UserHandle(userId); 110 mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user); 111 Bitmap bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default); 112 mUserManager.setUserIcon(userId, bitmap); 113 // Add shared accounts 114 AccountManager am = AccountManager.get(getActivity()); 115 Account[] accounts = am.getAccounts(); 116 if (accounts != null) { 117 for (Account account : accounts) { 118 am.addSharedAccount(account, user); 119 } 120 } 121 return restrictedUserInfo; 122 } 123 124 @Override 125 protected void onPostExecute(UserInfo result) { 126 if (result == null) { 127 return; 128 } 129 mRestrictedUserInfo = result; 130 UserSwitchListenerService.updateLaunchPoint(getActivity(), true); 131 int userId = result.id; 132 if (result.isRestricted() && mIsOwner) { 133 DialogFragment dialogFragment = UserAppRestrictionsDialogFragment.newInstance( 134 getActivity(), userId, true); 135 DialogFragment.add(getFragmentManager(), dialogFragment); 136 mMainMenuDialogFragment.setActions(getMainMenuActions()); 137 } 138 } 139 }; 140 141 @Override 142 public void onCreate(Bundle savedInstanceState) { 143 super.onCreate(savedInstanceState); 144 mHandler = new Handler(); 145 mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 146 mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); 147 mRestrictedUserInfo = findRestrictedUser(mUserManager); 148 mConfigAppsAction = createConfigAppsAction(-1); 149 mMainMenuDialogFragment = new DialogFragment.Builder() 150 .title(getString(R.string.launcher_restricted_profile_app_name)) 151 .description(getString(R.string.user_add_profile_item_summary)) 152 .iconResourceId(getIconResource()) 153 .iconBackgroundColor(getResources().getColor(R.color.icon_background)) 154 .actions(getMainMenuActions()).build(); 155 mMainMenuDialogFragment.setListener(this); 156 DialogFragment.add(getFragmentManager(), mMainMenuDialogFragment); 157 if (savedInstanceState != null) { 158 mPinMode = savedInstanceState.getInt(STATE_PIN_MODE, PIN_MODE_NONE); 159 } 160 } 161 162 @Override 163 public void onResume() { 164 super.onResume(); 165 if (mRestrictedUserInfo != null && (mAppLoadingTask == null 166 || mAppLoadingTask.getStatus() == AsyncTask.Status.FINISHED)) { 167 mAppLoadingTask = new AppLoadingTask(getActivity(), mRestrictedUserInfo.id, false, mIPm, 168 this); 169 mAppLoadingTask.execute((Void[]) null); 170 } 171 } 172 173 @Override 174 public void onSaveInstanceState(Bundle outState) { 175 super.onSaveInstanceState(outState); 176 outState.putInt(STATE_PIN_MODE, mPinMode); 177 } 178 179 @Override 180 public void onPackageEnableChanged(String packageName, boolean enabled) { 181 } 182 183 @Override 184 public void onActionsLoaded(ArrayList<Action> actions) { 185 int allowedApps = 0; 186 for(Action action : actions) { 187 if(action.isChecked()) { 188 allowedApps++; 189 } 190 } 191 mConfigAppsAction = createConfigAppsAction(allowedApps); 192 if (mConfigDialogFragment != null) { 193 mConfigDialogFragment.setActions(getConfigActions()); 194 } 195 } 196 197 @Override 198 public void onActionClicked(Action action) { 199 if (ACTION_RESTRICTED_PROFILE_SWITCH_TO.equals(action.getKey())) { 200 switchUserNow(mRestrictedUserInfo.id); 201 getActivity().finish(); 202 } else if (ACTION_RESTRICTED_PROFILE_SWITCH_OUT.equals(action.getKey())) { 203 if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) { 204 return; 205 } 206 mPinMode = PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT; 207 RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment = 208 RestrictedProfilePinDialogFragment.newInstance( 209 PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN); 210 restrictedProfilePinDialogFragment.setTargetFragment(this, 0); 211 restrictedProfilePinDialogFragment.show(getFragmentManager(), 212 PinDialogFragment.DIALOG_TAG); 213 } else if (ACTION_RESTRICTED_PROFILE_CHANGE_PASSWORD.equals(action.getKey())) { 214 if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) { 215 return; 216 } 217 mPinMode = PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD; 218 RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment = 219 RestrictedProfilePinDialogFragment.newInstance( 220 PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN); 221 restrictedProfilePinDialogFragment.setTargetFragment(this, 0); 222 restrictedProfilePinDialogFragment.show(getFragmentManager(), 223 PinDialogFragment.DIALOG_TAG); 224 } else if (ACTION_RESTRICTED_PROFILE_CONFIG.equals(action.getKey())) { 225 mConfigDialogFragment = new DialogFragment.Builder() 226 .title(getString(R.string.restricted_profile_configure_title)) 227 .iconResourceId(getIconResource()) 228 .iconBackgroundColor(getResources().getColor(R.color.icon_background)) 229 .actions(getConfigActions()).build(); 230 mConfigDialogFragment.setListener(this); 231 DialogFragment.add(getFragmentManager(), mConfigDialogFragment); 232 } else if (ACTION_RESTRICTED_PROFILE_CONFIG_APPS.equals(action.getKey())) { 233 DialogFragment dialogFragment = UserAppRestrictionsDialogFragment.newInstance( 234 getActivity(), mRestrictedUserInfo.id, false); 235 DialogFragment.add(getFragmentManager(), dialogFragment); 236 } else if (ACTION_RESTRICTED_PROFILE_DELETE.equals(action.getKey())) { 237 if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) { 238 return; 239 } 240 mPinMode = PIN_MODE_RESTRICTED_PROFILE_DELETE; 241 RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment = 242 RestrictedProfilePinDialogFragment.newInstance( 243 PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN); 244 restrictedProfilePinDialogFragment.setTargetFragment(this, 0); 245 restrictedProfilePinDialogFragment.show(getFragmentManager(), 246 PinDialogFragment.DIALOG_TAG); 247 } else if (ACTION_RESTRICTED_PROFILE_DELETE_CONFIRM.equals(action.getKey())) { 248 // TODO remove once we confirm it's not needed 249 removeRestrictedUser(); 250 LockPatternUtils lpu = new LockPatternUtils(getActivity()); 251 lpu.clearLock(UserHandle.myUserId()); 252 } else if (ACTION_RESTRICTED_PROFILE_DELETE_CANCEL.equals(action.getKey())) { 253 // TODO remove once we confirm it's not needed 254 getActivity().onBackPressed(); 255 } else if (ACTION_RESTRICTED_PROFILE_CREATE.equals(action.getKey())) { 256 if (hasLockscreenSecurity(new LockPatternUtils(getActivity()))) { 257 addRestrictedUser(); 258 } else { 259 launchChooseLockscreen(); 260 } 261 } 262 } 263 264 /** 265 * The description string that should be used for an action that launches the restricted profile 266 * activity. 267 * 268 * @param context used to get the appropriate string. 269 * @return the description string that should be used for an action that launches the restricted 270 * profile activity. 271 */ 272 public static String getActionDescription(Context context) { 273 return context.getString(isRestrictedProfileInEffect(context) ? R.string.on : R.string.off); 274 } 275 276 public static boolean isRestrictedProfileInEffect(Context context) { 277 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 278 UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId()); 279 return userInfo.isRestricted(); 280 } 281 282 /* package */ static void switchUserNow(int userId) { 283 try { 284 ActivityManagerNative.getDefault().switchUser(userId); 285 } catch (RemoteException re) { 286 Log.e(TAG, "Caught exception while switching user! ", re); 287 } 288 } 289 290 /* package */ static int getIconResource() { 291 return R.drawable.ic_settings_restricted_profile; 292 } 293 294 /* package */ static UserInfo findRestrictedUser(UserManager userManager) { 295 for (UserInfo userInfo : userManager.getUsers()) { 296 if (userInfo.isRestricted()) { 297 return userInfo; 298 } 299 } 300 return null; 301 } 302 303 // RestrictedProfilePinDialogFragment.Callback methods 304 @Override 305 public void saveLockPassword(String pin, int quality) { 306 new LockPatternUtils(getActivity()).saveLockPassword(pin, null, quality, 307 UserHandle.myUserId()); 308 } 309 310 @Override 311 public boolean checkPassword(String password, int userId) { 312 try { 313 return getLockSettings().checkPassword(password, userId).getResponseCode() 314 == VerifyCredentialResponse.RESPONSE_OK; 315 } catch (final RemoteException e) { 316 // ignore 317 } 318 return false; 319 } 320 321 @Override 322 public boolean hasLockscreenSecurity() { 323 return RestrictedProfileDialogFragment.hasLockscreenSecurity( 324 new LockPatternUtils(getActivity())); 325 } 326 327 @Override 328 public void pinFragmentDone(boolean success) { 329 switch (mPinMode) { 330 case PIN_MODE_CHOOSE_LOCKSCREEN: 331 if (success) { 332 addRestrictedUser(); 333 } 334 break; 335 case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT: 336 if (success) { 337 switchUserNow(UserHandle.USER_OWNER); 338 getActivity().finish(); 339 } 340 break; 341 case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD: 342 // do nothing 343 break; 344 case PIN_MODE_RESTRICTED_PROFILE_DELETE: 345 if (success) { 346 removeRestrictedUser(); 347 new LockPatternUtils(getActivity()).clearLock(UserHandle.myUserId()); 348 } 349 break; 350 } 351 } 352 353 private ILockSettings getLockSettings() { 354 if (mLockSettingsService == null) { 355 mLockSettingsService = ILockSettings.Stub.asInterface( 356 ServiceManager.getService("lock_settings")); 357 } 358 return mLockSettingsService; 359 } 360 361 private ArrayList<Action> getMainMenuActions() { 362 ArrayList<Action> actions = new ArrayList<>(); 363 if (mRestrictedUserInfo != null) { 364 if (mIsOwner) { 365 actions.add(new Action.Builder() 366 .key(ACTION_RESTRICTED_PROFILE_SWITCH_TO) 367 .title(getString(R.string.restricted_profile_switch_to)) 368 .build()); 369 actions.add(new Action.Builder() 370 .key(ACTION_RESTRICTED_PROFILE_CONFIG) 371 .title(getString(R.string.restricted_profile_configure_title)) 372 .build()); 373 actions.add(new Action.Builder() 374 .key(ACTION_RESTRICTED_PROFILE_DELETE) 375 .title(getString(R.string.restricted_profile_delete_title)) 376 .build()); 377 } else { 378 actions.add(new Action.Builder() 379 .key(ACTION_RESTRICTED_PROFILE_SWITCH_OUT) 380 .title(getString(R.string.restricted_profile_switch_out)) 381 .build()); 382 } 383 } else { 384 actions.add(new Action.Builder() 385 .key(ACTION_RESTRICTED_PROFILE_CREATE) 386 .title(getString(R.string.restricted_profile_configure_title)) 387 .build()); 388 } 389 return actions; 390 } 391 392 private ArrayList<Action> getConfigActions() { 393 ArrayList<Action> actions = new ArrayList<>(); 394 actions.add(new Action.Builder() 395 .key(ACTION_RESTRICTED_PROFILE_CHANGE_PASSWORD) 396 .title(getString(R.string.restricted_profile_change_password_title)) 397 .build()); 398 actions.add(mConfigAppsAction); 399 return actions; 400 } 401 402 private Action createConfigAppsAction(int allowedApps) { 403 String description = allowedApps >= 0 ? getResources().getQuantityString( 404 R.plurals.restricted_profile_configure_apps_description, allowedApps, allowedApps) 405 : getString(R.string.restricted_profile_configure_apps_description_loading); 406 return new Action.Builder() 407 .key(ACTION_RESTRICTED_PROFILE_CONFIG_APPS) 408 .title(getString(R.string.restricted_profile_configure_apps_title)) 409 .description(description) 410 .build(); 411 } 412 413 private static boolean hasLockscreenSecurity(LockPatternUtils lpu) { 414 return lpu.isLockPasswordEnabled(UserHandle.myUserId()) 415 || lpu.isLockPatternEnabled(UserHandle.myUserId()); 416 } 417 418 private void launchChooseLockscreen() { 419 if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) { 420 return; 421 } 422 mPinMode = PIN_MODE_CHOOSE_LOCKSCREEN; 423 RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment = 424 RestrictedProfilePinDialogFragment.newInstance( 425 PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN); 426 restrictedProfilePinDialogFragment.setTargetFragment(this, 0); 427 restrictedProfilePinDialogFragment.show(getFragmentManager(), PinDialogFragment.DIALOG_TAG); 428 } 429 430 private void removeRestrictedUser() { 431 mHandler.post(new Runnable() { 432 @Override 433 public void run() { 434 mUserManager.removeUser(mRestrictedUserInfo.id); 435 // pop confirm dialog 436 mRestrictedUserInfo = null; 437 UserSwitchListenerService.updateLaunchPoint(getActivity(), false); 438 mMainMenuDialogFragment.setActions(getMainMenuActions()); 439 getFragmentManager().popBackStack(); 440 } 441 }); 442 } 443 444 private Bitmap createBitmapFromDrawable(int resId) { 445 Drawable icon = getActivity().getDrawable(resId); 446 icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); 447 Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), 448 Bitmap.Config.ARGB_8888); 449 icon.draw(new Canvas(bitmap)); 450 return bitmap; 451 } 452 453 private void addRestrictedUser() { 454 if (AsyncTask.Status.PENDING == mAddUserAsyncTask.getStatus()) { 455 mAddUserAsyncTask.execute((Void[]) null); 456 } 457 } 458 } 459