1 /* 2 * Copyright (C) 2016 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.system; 18 19 import android.accounts.AccountManager; 20 import android.app.ActivityManagerNative; 21 import android.app.Fragment; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 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; 38 import android.support.annotation.DrawableRes; 39 import android.support.annotation.IntDef; 40 import android.support.v17.preference.LeanbackPreferenceFragment; 41 import android.support.v17.preference.LeanbackSettingsFragment; 42 import android.support.v7.preference.Preference; 43 import android.support.v7.preference.PreferenceGroup; 44 import android.support.v7.preference.TwoStatePreference; 45 import android.text.TextUtils; 46 import android.util.Log; 47 48 import com.android.internal.widget.ILockSettings; 49 import com.android.internal.widget.LockPatternUtils; 50 import com.android.internal.widget.VerifyCredentialResponse; 51 import com.android.tv.settings.R; 52 import com.android.tv.settings.dialog.PinDialogFragment; 53 import com.android.tv.settings.users.AppRestrictionsFragment; 54 import com.android.tv.settings.users.RestrictedProfilePinDialogFragment; 55 import com.android.tv.settings.users.UserSwitchListenerService; 56 57 import java.lang.annotation.Retention; 58 import java.lang.annotation.RetentionPolicy; 59 import java.util.List; 60 61 public class SecurityFragment extends LeanbackPreferenceFragment 62 implements RestrictedProfilePinDialogFragment.Callback, 63 UnknownSourcesConfirmationFragment.Callback { 64 65 private static final String TAG = "SecurityFragment"; 66 67 private static final String KEY_UNKNOWN_SOURCES = "unknown_sources"; 68 private static final String KEY_VERIFY_APPS = "verify_apps"; 69 private static final String KEY_RESTRICTED_PROFILE_GROUP = "restricted_profile_group"; 70 private static final String KEY_RESTRICTED_PROFILE_ENTER = "restricted_profile_enter"; 71 private static final String KEY_RESTRICTED_PROFILE_EXIT = "restricted_profile_exit"; 72 private static final String KEY_RESTRICTED_PROFILE_APPS = "restricted_profile_apps"; 73 private static final String KEY_RESTRICTED_PROFILE_PIN = "restricted_profile_pin"; 74 private static final String KEY_RESTRICTED_PROFILE_CREATE = "restricted_profile_create"; 75 private static final String KEY_RESTRICTED_PROFILE_DELETE = "restricted_profile_delete"; 76 77 private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; 78 79 @Retention(RetentionPolicy.SOURCE) 80 @IntDef({PIN_MODE_CHOOSE_LOCKSCREEN, 81 PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT, 82 PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD, 83 PIN_MODE_RESTRICTED_PROFILE_DELETE}) 84 private @interface PinMode {} 85 private static final int PIN_MODE_CHOOSE_LOCKSCREEN = 1; 86 private static final int PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT = 2; 87 private static final int PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD = 3; 88 private static final int PIN_MODE_RESTRICTED_PROFILE_DELETE = 4; 89 90 private TwoStatePreference mUnknownSourcesPref; 91 private TwoStatePreference mVerifyAppsPref; 92 private PreferenceGroup mRestrictedProfileGroup; 93 private Preference mRestrictedProfileEnterPref; 94 private Preference mRestrictedProfileExitPref; 95 private Preference mRestrictedProfileAppsPref; 96 private Preference mRestrictedProfilePinPref; 97 private Preference mRestrictedProfileCreatePref; 98 private Preference mRestrictedProfileDeletePref; 99 100 private UserManager mUserManager; 101 private UserInfo mRestrictedUserInfo; 102 private ILockSettings mLockSettingsService; 103 104 private static CreateRestrictedProfileTask sCreateRestrictedProfileTask; 105 106 private final Handler mHandler = new Handler(); 107 newInstance()108 public static SecurityFragment newInstance() { 109 return new SecurityFragment(); 110 } 111 112 @Override onCreate(Bundle savedInstanceState)113 public void onCreate(Bundle savedInstanceState) { 114 mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 115 super.onCreate(savedInstanceState); 116 } 117 118 @Override onResume()119 public void onResume() { 120 super.onResume(); 121 refresh(); 122 } 123 124 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)125 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 126 setPreferencesFromResource(R.xml.security, null); 127 128 mUnknownSourcesPref = (TwoStatePreference) findPreference(KEY_UNKNOWN_SOURCES); 129 mVerifyAppsPref = (TwoStatePreference) findPreference(KEY_VERIFY_APPS); 130 mRestrictedProfileGroup = (PreferenceGroup) findPreference(KEY_RESTRICTED_PROFILE_GROUP); 131 mRestrictedProfileEnterPref = findPreference(KEY_RESTRICTED_PROFILE_ENTER); 132 mRestrictedProfileExitPref = findPreference(KEY_RESTRICTED_PROFILE_EXIT); 133 mRestrictedProfileAppsPref = findPreference(KEY_RESTRICTED_PROFILE_APPS); 134 mRestrictedProfilePinPref = findPreference(KEY_RESTRICTED_PROFILE_PIN); 135 mRestrictedProfileCreatePref = findPreference(KEY_RESTRICTED_PROFILE_CREATE); 136 mRestrictedProfileDeletePref = findPreference(KEY_RESTRICTED_PROFILE_DELETE); 137 } 138 refresh()139 private void refresh() { 140 if (isRestrictedProfileInEffect(mUserManager)) { 141 // We are in restricted profile 142 mUnknownSourcesPref.setVisible(false); 143 mVerifyAppsPref.setVisible(false); 144 145 mRestrictedProfileGroup.setVisible(true); 146 mRestrictedProfileEnterPref.setVisible(false); 147 mRestrictedProfileExitPref.setVisible(true); 148 mRestrictedProfileAppsPref.setVisible(false); 149 mRestrictedProfilePinPref.setVisible(false); 150 mRestrictedProfileCreatePref.setVisible(false); 151 mRestrictedProfileDeletePref.setVisible(false); 152 } else if (getRestrictedUser() != null) { 153 // Not in restricted profile, but it exists 154 mUnknownSourcesPref.setVisible(true); 155 mVerifyAppsPref.setVisible(shouldShowVerifierSetting()); 156 157 mRestrictedProfileGroup.setVisible(true); 158 mRestrictedProfileEnterPref.setVisible(true); 159 mRestrictedProfileExitPref.setVisible(false); 160 mRestrictedProfileAppsPref.setVisible(true); 161 mRestrictedProfilePinPref.setVisible(true); 162 mRestrictedProfileCreatePref.setVisible(false); 163 mRestrictedProfileDeletePref.setVisible(true); 164 165 AppRestrictionsFragment.prepareArgs(mRestrictedProfileAppsPref.getExtras(), 166 getRestrictedUser().id, false); 167 } else if (UserManager.supportsMultipleUsers()) { 168 // Not in restricted profile, and it doesn't exist 169 mUnknownSourcesPref.setVisible(true); 170 mVerifyAppsPref.setVisible(shouldShowVerifierSetting()); 171 172 mRestrictedProfileGroup.setVisible(true); 173 mRestrictedProfileEnterPref.setVisible(false); 174 mRestrictedProfileExitPref.setVisible(false); 175 mRestrictedProfileAppsPref.setVisible(false); 176 mRestrictedProfilePinPref.setVisible(false); 177 mRestrictedProfileCreatePref.setVisible(true); 178 mRestrictedProfileDeletePref.setVisible(false); 179 } else { 180 // Not in restricted profile, and can't create one either 181 mUnknownSourcesPref.setVisible(true); 182 mVerifyAppsPref.setVisible(shouldShowVerifierSetting()); 183 184 mRestrictedProfileGroup.setVisible(false); 185 mRestrictedProfileEnterPref.setVisible(false); 186 mRestrictedProfileExitPref.setVisible(false); 187 mRestrictedProfileAppsPref.setVisible(false); 188 mRestrictedProfilePinPref.setVisible(false); 189 mRestrictedProfileCreatePref.setVisible(false); 190 mRestrictedProfileDeletePref.setVisible(false); 191 } 192 193 mUnknownSourcesPref.setEnabled(!isUnknownSourcesBlocked()); 194 mUnknownSourcesPref.setChecked(isUnknownSourcesAllowed()); 195 mVerifyAppsPref.setChecked(isVerifyAppsEnabled()); 196 mVerifyAppsPref.setEnabled(isVerifierInstalled()); 197 } 198 199 @Override onPreferenceTreeClick(Preference preference)200 public boolean onPreferenceTreeClick(Preference preference) { 201 final String key = preference.getKey(); 202 if (TextUtils.isEmpty(key)) { 203 return super.onPreferenceTreeClick(preference); 204 } 205 switch (key) { 206 case KEY_UNKNOWN_SOURCES: 207 // TODO: confirmation dialog 208 if (mUnknownSourcesPref.isChecked()) { 209 /** Launches {@link UnknownSourcesConfirmationFragment} */ 210 super.onPreferenceTreeClick(preference); 211 } else { 212 setUnknownSourcesAllowed(false); 213 } 214 return true; 215 case KEY_VERIFY_APPS: 216 setVerifyAppsEnabled(mVerifyAppsPref.isChecked()); 217 return true; 218 case KEY_RESTRICTED_PROFILE_ENTER: 219 final UserInfo restrictedUser = getRestrictedUser(); 220 if (restrictedUser == null) { 221 Log.e(TAG, "Tried to enter non-existent restricted user"); 222 return true; 223 } 224 switchUserNow(restrictedUser.id); 225 getActivity().finish(); 226 return true; 227 case KEY_RESTRICTED_PROFILE_EXIT: 228 launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT); 229 return true; 230 case KEY_RESTRICTED_PROFILE_PIN: 231 launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD); 232 return true; 233 case KEY_RESTRICTED_PROFILE_CREATE: 234 if (hasLockscreenSecurity(new LockPatternUtils(getActivity()))) { 235 addRestrictedUser(); 236 } else { 237 launchPinDialog(PIN_MODE_CHOOSE_LOCKSCREEN); 238 } 239 return true; 240 case KEY_RESTRICTED_PROFILE_DELETE: 241 launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_DELETE); 242 return true; 243 } 244 return super.onPreferenceTreeClick(preference); 245 } 246 isUnknownSourcesAllowed()247 private boolean isUnknownSourcesAllowed() { 248 return Settings.Secure.getInt(getContext().getContentResolver(), 249 Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0; 250 } 251 setUnknownSourcesAllowed(boolean enabled)252 private void setUnknownSourcesAllowed(boolean enabled) { 253 if (isUnknownSourcesBlocked()) { 254 return; 255 } 256 // Change the system setting 257 Settings.Secure.putInt(getContext().getContentResolver(), 258 Settings.Secure.INSTALL_NON_MARKET_APPS, enabled ? 1 : 0); 259 } 260 isUnknownSourcesBlocked()261 private boolean isUnknownSourcesBlocked() { 262 final UserManager um = (UserManager) getContext().getSystemService(Context.USER_SERVICE); 263 return um.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); 264 } 265 266 @Override onConfirmUnknownSources(boolean success)267 public void onConfirmUnknownSources(boolean success) { 268 setUnknownSourcesAllowed(success); 269 270 mUnknownSourcesPref.setChecked(success); 271 } 272 isVerifyAppsEnabled()273 private boolean isVerifyAppsEnabled() { 274 return Settings.Global.getInt(getContext().getContentResolver(), 275 Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) > 0 && isVerifierInstalled(); 276 } 277 setVerifyAppsEnabled(boolean enable)278 private void setVerifyAppsEnabled(boolean enable) { 279 Settings.Global.putInt(getContext().getContentResolver(), 280 Settings.Global.PACKAGE_VERIFIER_ENABLE, enable ? 1 : 0); 281 } 282 isVerifierInstalled()283 private boolean isVerifierInstalled() { 284 final PackageManager pm = getContext().getPackageManager(); 285 final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); 286 verification.setType(PACKAGE_MIME_TYPE); 287 verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 288 final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(verification, 0); 289 return receivers.size() > 0; 290 } 291 shouldShowVerifierSetting()292 private boolean shouldShowVerifierSetting() { 293 return Settings.Global.getInt(getContext().getContentResolver(), 294 Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1) > 0; 295 } 296 launchPinDialog(@inMode int pinMode)297 private void launchPinDialog(@PinMode int pinMode) { 298 @PinDialogFragment.PinDialogType 299 int pinDialogMode; 300 301 switch (pinMode) { 302 case PIN_MODE_CHOOSE_LOCKSCREEN: 303 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN; 304 break; 305 case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT: 306 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN; 307 break; 308 case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD: 309 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN; 310 break; 311 case PIN_MODE_RESTRICTED_PROFILE_DELETE: 312 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN; 313 break; 314 default: 315 throw new IllegalArgumentException("Unknown pin mode: " + pinMode); 316 } 317 318 RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment = 319 RestrictedProfilePinDialogFragment.newInstance(pinDialogMode); 320 restrictedProfilePinDialogFragment.setTargetFragment(this, pinMode); 321 restrictedProfilePinDialogFragment.show(getFragmentManager(), 322 PinDialogFragment.DIALOG_TAG); 323 } 324 325 @Override saveLockPassword(String pin, int quality)326 public void saveLockPassword(String pin, int quality) { 327 new LockPatternUtils(getActivity()).saveLockPassword(pin, null, quality, 328 UserHandle.myUserId()); 329 } 330 331 @Override checkPassword(String password, int userId)332 public boolean checkPassword(String password, int userId) { 333 try { 334 return getLockSettings().checkPassword(password, userId).getResponseCode() 335 == VerifyCredentialResponse.RESPONSE_OK; 336 } catch (final RemoteException e) { 337 // ignore 338 } 339 return false; 340 } 341 342 @Override hasLockscreenSecurity()343 public boolean hasLockscreenSecurity() { 344 return hasLockscreenSecurity(new LockPatternUtils(getActivity())); 345 } 346 getLockSettings()347 private ILockSettings getLockSettings() { 348 if (mLockSettingsService == null) { 349 mLockSettingsService = ILockSettings.Stub.asInterface( 350 ServiceManager.getService("lock_settings")); 351 } 352 return mLockSettingsService; 353 } 354 hasLockscreenSecurity(LockPatternUtils lpu)355 private static boolean hasLockscreenSecurity(LockPatternUtils lpu) { 356 return lpu.isLockPasswordEnabled(UserHandle.myUserId()) 357 || lpu.isLockPatternEnabled(UserHandle.myUserId()); 358 } 359 360 @Override pinFragmentDone(int requestCode, boolean success)361 public void pinFragmentDone(int requestCode, boolean success) { 362 switch (requestCode) { 363 case PIN_MODE_CHOOSE_LOCKSCREEN: 364 if (success) { 365 addRestrictedUser(); 366 } 367 break; 368 case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT: 369 if (success) { 370 UserInfo myUserInfo = 371 UserManager.get(getActivity()).getUserInfo(UserHandle.myUserId()); 372 if (myUserInfo == null || 373 myUserInfo.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) { 374 switchUserNow(UserHandle.USER_SYSTEM); 375 } else { 376 switchUserNow(myUserInfo.restrictedProfileParentId); 377 } 378 getActivity().finish(); 379 } 380 break; 381 case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD: 382 // do nothing 383 break; 384 case PIN_MODE_RESTRICTED_PROFILE_DELETE: 385 if (success) { 386 removeRestrictedUser(); 387 new LockPatternUtils(getActivity()).clearLock(UserHandle.myUserId()); 388 } 389 break; 390 } 391 } 392 findRestrictedUser(UserManager userManager)393 public static UserInfo findRestrictedUser(UserManager userManager) { 394 for (UserInfo userInfo : userManager.getUsers()) { 395 if (userInfo.isRestricted()) { 396 return userInfo; 397 } 398 } 399 return null; 400 } 401 getRestrictedUser()402 private UserInfo getRestrictedUser() { 403 if (mRestrictedUserInfo == null) { 404 mRestrictedUserInfo = findRestrictedUser(mUserManager); 405 } 406 return mRestrictedUserInfo; 407 } 408 switchUserNow(int userId)409 private static void switchUserNow(int userId) { 410 try { 411 ActivityManagerNative.getDefault().switchUser(userId); 412 } catch (RemoteException re) { 413 Log.e(TAG, "Caught exception while switching user! ", re); 414 } 415 } 416 addRestrictedUser()417 private void addRestrictedUser() { 418 if (sCreateRestrictedProfileTask == null) { 419 sCreateRestrictedProfileTask = new CreateRestrictedProfileTask(getContext(), 420 mUserManager); 421 sCreateRestrictedProfileTask.execute(); 422 } 423 } 424 removeRestrictedUser()425 private void removeRestrictedUser() { 426 final UserInfo restrictedUser = getRestrictedUser(); 427 if (restrictedUser == null) { 428 Log.w(TAG, "No restricted user to remove?"); 429 return; 430 } 431 final int restrictedUserHandle = restrictedUser.id; 432 mRestrictedUserInfo = null; 433 mHandler.post(new Runnable() { 434 @Override 435 public void run() { 436 mUserManager.removeUser(restrictedUserHandle); 437 UserSwitchListenerService.updateLaunchPoint(getActivity(), false); 438 refresh(); 439 } 440 }); 441 } 442 isRestrictedProfileInEffect(Context context)443 public static boolean isRestrictedProfileInEffect(Context context) { 444 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 445 UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId()); 446 return userInfo.isRestricted(); 447 } 448 isRestrictedProfileInEffect(UserManager userManager)449 private static boolean isRestrictedProfileInEffect(UserManager userManager) { 450 UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId()); 451 return userInfo.isRestricted(); 452 } 453 454 private class CreateRestrictedProfileTask extends AsyncTask<Void, Void, UserInfo> { 455 private final Context mContext; 456 private final UserManager mUserManager; 457 CreateRestrictedProfileTask(Context context, UserManager userManager)458 CreateRestrictedProfileTask(Context context, UserManager userManager) { 459 mContext = context; 460 mUserManager = userManager; 461 } 462 463 @Override doInBackground(Void... params)464 protected UserInfo doInBackground(Void... params) { 465 UserInfo restrictedUserInfo = mUserManager.createProfileForUser( 466 mContext.getString(R.string.user_new_profile_name), 467 UserInfo.FLAG_RESTRICTED, UserHandle.myUserId()); 468 if (restrictedUserInfo == null) { 469 Log.wtf(TAG, "Got back a null user handle!"); 470 return null; 471 } 472 int userId = restrictedUserInfo.id; 473 UserHandle user = new UserHandle(userId); 474 mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user); 475 Bitmap bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default); 476 mUserManager.setUserIcon(userId, bitmap); 477 // Add shared accounts 478 AccountManager.get(mContext).addSharedAccountsFromParentUser( 479 UserHandle.of(UserHandle.myUserId()), user); 480 return restrictedUserInfo; 481 } 482 483 @Override onPostExecute(UserInfo result)484 protected void onPostExecute(UserInfo result) { 485 sCreateRestrictedProfileTask = null; 486 if (result == null) { 487 return; 488 } 489 UserSwitchListenerService.updateLaunchPoint(mContext, true); 490 int userId = result.id; 491 if (result.isRestricted() 492 && isAdded() 493 && result.restrictedProfileParentId == UserHandle.myUserId()) { 494 final AppRestrictionsFragment restrictionsFragment = 495 AppRestrictionsFragment.newInstance(userId, true); 496 final Fragment settingsFragment = getCallbackFragment(); 497 if (settingsFragment instanceof LeanbackSettingsFragment) { 498 ((LeanbackSettingsFragment) settingsFragment) 499 .startPreferenceFragment(restrictionsFragment); 500 } else { 501 throw new IllegalStateException("Didn't find fragment of expected type: " 502 + settingsFragment); 503 } 504 } 505 } 506 createBitmapFromDrawable(@rawableRes int resId)507 private Bitmap createBitmapFromDrawable(@DrawableRes int resId) { 508 Drawable icon = mContext.getDrawable(resId); 509 if (icon == null) { 510 throw new IllegalArgumentException("Drawable is missing!"); 511 } 512 icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); 513 Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), 514 Bitmap.Config.ARGB_8888); 515 icon.draw(new Canvas(bitmap)); 516 return bitmap; 517 } 518 } 519 } 520