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