Home | History | Annotate | Download | only in users
      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