Home | History | Annotate | Download | only in system
      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 
    108     public static SecurityFragment newInstance() {
    109         return new SecurityFragment();
    110     }
    111 
    112     @Override
    113     public void onCreate(Bundle savedInstanceState) {
    114         mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
    115         super.onCreate(savedInstanceState);
    116     }
    117 
    118     @Override
    119     public void onResume() {
    120         super.onResume();
    121         refresh();
    122     }
    123 
    124     @Override
    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 
    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
    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 
    247     private boolean isUnknownSourcesAllowed() {
    248         return Settings.Secure.getInt(getContext().getContentResolver(),
    249                 Settings.Secure.INSTALL_NON_MARKET_APPS, 0) > 0;
    250     }
    251 
    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 
    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
    267     public void onConfirmUnknownSources(boolean success) {
    268         setUnknownSourcesAllowed(success);
    269 
    270         mUnknownSourcesPref.setChecked(success);
    271     }
    272 
    273     private boolean isVerifyAppsEnabled() {
    274         return Settings.Global.getInt(getContext().getContentResolver(),
    275                 Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) > 0 && isVerifierInstalled();
    276     }
    277 
    278     private void setVerifyAppsEnabled(boolean enable) {
    279         Settings.Global.putInt(getContext().getContentResolver(),
    280                 Settings.Global.PACKAGE_VERIFIER_ENABLE, enable ? 1 : 0);
    281     }
    282 
    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 
    292     private boolean shouldShowVerifierSetting() {
    293         return Settings.Global.getInt(getContext().getContentResolver(),
    294                 Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE, 1) > 0;
    295     }
    296 
    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
    326     public void saveLockPassword(String pin, int quality) {
    327         new LockPatternUtils(getActivity()).saveLockPassword(pin, null, quality,
    328                 UserHandle.myUserId());
    329     }
    330 
    331     @Override
    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
    343     public boolean hasLockscreenSecurity() {
    344         return hasLockscreenSecurity(new LockPatternUtils(getActivity()));
    345     }
    346 
    347     private ILockSettings getLockSettings() {
    348         if (mLockSettingsService == null) {
    349             mLockSettingsService = ILockSettings.Stub.asInterface(
    350                     ServiceManager.getService("lock_settings"));
    351         }
    352         return mLockSettingsService;
    353     }
    354 
    355     private static boolean hasLockscreenSecurity(LockPatternUtils lpu) {
    356         return lpu.isLockPasswordEnabled(UserHandle.myUserId())
    357                 || lpu.isLockPatternEnabled(UserHandle.myUserId());
    358     }
    359 
    360     @Override
    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 
    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 
    402     private UserInfo getRestrictedUser() {
    403         if (mRestrictedUserInfo == null) {
    404             mRestrictedUserInfo = findRestrictedUser(mUserManager);
    405         }
    406         return mRestrictedUserInfo;
    407     }
    408 
    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 
    417     private void addRestrictedUser() {
    418         if (sCreateRestrictedProfileTask == null) {
    419             sCreateRestrictedProfileTask = new CreateRestrictedProfileTask(getContext(),
    420                     mUserManager);
    421             sCreateRestrictedProfileTask.execute();
    422         }
    423     }
    424 
    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 
    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 
    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 
    458         CreateRestrictedProfileTask(Context context, UserManager userManager) {
    459             mContext = context;
    460             mUserManager = userManager;
    461         }
    462 
    463         @Override
    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
    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 
    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