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