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