blob: 3a086b7e7f2e8788c06766365b7e3359d8cceff9 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tv.settings.users;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.Fragment;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Secure;
import android.util.Log;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.tv.settings.R;
import com.android.tv.settings.dialog.DialogFragment;
import com.android.tv.settings.dialog.DialogFragment.Action;
import com.android.tv.settings.dialog.PinDialogFragment;
import java.util.ArrayList;
/**
* Activity that allows the configuration of a user's restricted profile.
*/
public class RestrictedProfileDialogFragment extends Fragment implements Action.Listener,
AppLoadingTask.Listener, RestrictedProfilePinDialogFragment.Callback {
private static final String TAG = "RestrictedProfile";
private static final String ACTION_RESTRICTED_PROFILE_SETUP_LOCKSCREEN =
"restricted_setup_locakscreen";
private static final String ACTION_RESTRICTED_PROFILE_CREATE = "restricted_profile_create";
private static final String ACTION_RESTRICTED_PROFILE_SWITCH_TO =
"restricted_profile_switch_to";
private static final String ACTION_RESTRICTED_PROFILE_SWITCH_OUT =
"restricted_profile_switch_out";
private static final String ACTION_RESTRICTED_PROFILE_CONFIG = "restricted_profile_config";
private static final String ACTION_RESTRICTED_PROFILE_CONFIG_APPS =
"restricted_profile_config_apps";
private static final String ACTION_RESTRICTED_PROFILE_CHANGE_PASSWORD =
"restricted_profile_change_password";
private static final String ACTION_RESTRICTED_PROFILE_DELETE = "restricted_profile_delete";
private static final String
ACTION_RESTRICTED_PROFILE_DELETE_CONFIRM = "restricted_profile_delete_confirm";
private static final String
ACTION_RESTRICTED_PROFILE_DELETE_CANCEL = "restricted_profile_delete_cancel";
private static final String STATE_PIN_MODE = "RestrictedProfileActivity.pinMode";
private static final int PIN_MODE_NONE = 0;
private static final int PIN_MODE_CHOOSE_LOCKSCREEN = 1;
private static final int PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT = 2;
private static final int PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD = 3;
private static final int PIN_MODE_RESTRICTED_PROFILE_DELETE = 4;
private UserManager mUserManager;
private UserInfo mRestrictedUserInfo;
private DialogFragment mMainMenuDialogFragment;
private ILockSettings mLockSettingsService;
private Handler mHandler;
private IPackageManager mIPm;
private AppLoadingTask mAppLoadingTask;
private Action mConfigAppsAction;
private DialogFragment mConfigDialogFragment;
private int mPinMode;
private final boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER;
private final AsyncTask<Void, Void, UserInfo> mAddUserAsyncTask =
new AsyncTask<Void, Void, UserInfo>() {
@Override
protected UserInfo doInBackground(Void... params) {
UserInfo restrictedUserInfo = mUserManager.createUser(
RestrictedProfileDialogFragment.this.getString(R.string.user_new_profile_name),
UserInfo.FLAG_RESTRICTED);
if (restrictedUserInfo == null) {
Log.wtf(TAG, "Got back a null user handle!");
return null;
}
int userId = restrictedUserInfo.id;
UserHandle user = new UserHandle(userId);
mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
Secure.putIntForUser(getActivity().getContentResolver(), Secure.LOCATION_MODE,
Secure.LOCATION_MODE_OFF, userId);
mUserManager.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user);
Bitmap bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default);
mUserManager.setUserIcon(userId, bitmap);
// Add shared accounts
AccountManager am = AccountManager.get(getActivity());
Account[] accounts = am.getAccounts();
if (accounts != null) {
for (Account account : accounts) {
am.addSharedAccount(account, user);
}
}
return restrictedUserInfo;
}
@Override
protected void onPostExecute(UserInfo result) {
if (result == null) {
return;
}
mRestrictedUserInfo = result;
UserSwitchListenerService.updateLaunchPoint(getActivity(), true);
int userId = result.id;
if (result.isRestricted() && mIsOwner) {
DialogFragment dialogFragment = UserAppRestrictionsDialogFragment.newInstance(
getActivity(), userId, true);
DialogFragment.add(getFragmentManager(), dialogFragment);
mMainMenuDialogFragment.setActions(getMainMenuActions());
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
mRestrictedUserInfo = findRestrictedUser(mUserManager);
mConfigAppsAction = createConfigAppsAction(-1);
mMainMenuDialogFragment = new DialogFragment.Builder()
.title(getString(R.string.launcher_restricted_profile_app_name))
.description(getString(R.string.user_add_profile_item_summary))
.iconResourceId(getIconResource())
.iconBackgroundColor(getResources().getColor(R.color.icon_background))
.actions(getMainMenuActions()).build();
mMainMenuDialogFragment.setListener(this);
DialogFragment.add(getFragmentManager(), mMainMenuDialogFragment);
if (savedInstanceState != null) {
mPinMode = savedInstanceState.getInt(STATE_PIN_MODE, PIN_MODE_NONE);
}
}
@Override
public void onResume() {
super.onResume();
if (mRestrictedUserInfo != null && (mAppLoadingTask == null
|| mAppLoadingTask.getStatus() == AsyncTask.Status.FINISHED)) {
mAppLoadingTask = new AppLoadingTask(getActivity(), mRestrictedUserInfo.id, false, mIPm,
this);
mAppLoadingTask.execute((Void[]) null);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_PIN_MODE, mPinMode);
}
@Override
public void onPackageEnableChanged(String packageName, boolean enabled) {
}
@Override
public void onActionsLoaded(ArrayList<Action> actions) {
int allowedApps = 0;
for(Action action : actions) {
if(action.isChecked()) {
allowedApps++;
}
}
mConfigAppsAction = createConfigAppsAction(allowedApps);
if (mConfigDialogFragment != null) {
mConfigDialogFragment.setActions(getConfigActions());
}
}
@Override
public void onActionClicked(Action action) {
if (ACTION_RESTRICTED_PROFILE_SWITCH_TO.equals(action.getKey())) {
switchUserNow(mRestrictedUserInfo.id);
getActivity().finish();
} else if (ACTION_RESTRICTED_PROFILE_SWITCH_OUT.equals(action.getKey())) {
if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) {
return;
}
mPinMode = PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT;
RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
RestrictedProfilePinDialogFragment.newInstance(
PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN);
restrictedProfilePinDialogFragment.setTargetFragment(this, 0);
restrictedProfilePinDialogFragment.show(getFragmentManager(),
PinDialogFragment.DIALOG_TAG);
} else if (ACTION_RESTRICTED_PROFILE_CHANGE_PASSWORD.equals(action.getKey())) {
if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) {
return;
}
mPinMode = PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD;
RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
RestrictedProfilePinDialogFragment.newInstance(
PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN);
restrictedProfilePinDialogFragment.setTargetFragment(this, 0);
restrictedProfilePinDialogFragment.show(getFragmentManager(),
PinDialogFragment.DIALOG_TAG);
} else if (ACTION_RESTRICTED_PROFILE_CONFIG.equals(action.getKey())) {
mConfigDialogFragment = new DialogFragment.Builder()
.title(getString(R.string.restricted_profile_configure_title))
.iconResourceId(getIconResource())
.iconBackgroundColor(getResources().getColor(R.color.icon_background))
.actions(getConfigActions()).build();
mConfigDialogFragment.setListener(this);
DialogFragment.add(getFragmentManager(), mConfigDialogFragment);
} else if (ACTION_RESTRICTED_PROFILE_CONFIG_APPS.equals(action.getKey())) {
DialogFragment dialogFragment = UserAppRestrictionsDialogFragment.newInstance(
getActivity(), mRestrictedUserInfo.id, false);
DialogFragment.add(getFragmentManager(), dialogFragment);
} else if (ACTION_RESTRICTED_PROFILE_DELETE.equals(action.getKey())) {
if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) {
return;
}
mPinMode = PIN_MODE_RESTRICTED_PROFILE_DELETE;
RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
RestrictedProfilePinDialogFragment.newInstance(
PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN);
restrictedProfilePinDialogFragment.setTargetFragment(this, 0);
restrictedProfilePinDialogFragment.show(getFragmentManager(),
PinDialogFragment.DIALOG_TAG);
} else if (ACTION_RESTRICTED_PROFILE_DELETE_CONFIRM.equals(action.getKey())) {
// TODO remove once we confirm it's not needed
removeRestrictedUser();
LockPatternUtils lpu = new LockPatternUtils(getActivity());
lpu.clearLock(UserHandle.myUserId());
} else if (ACTION_RESTRICTED_PROFILE_DELETE_CANCEL.equals(action.getKey())) {
// TODO remove once we confirm it's not needed
getActivity().onBackPressed();
} else if (ACTION_RESTRICTED_PROFILE_CREATE.equals(action.getKey())) {
if (hasLockscreenSecurity(new LockPatternUtils(getActivity()))) {
addRestrictedUser();
} else {
launchChooseLockscreen();
}
}
}
/**
* The description string that should be used for an action that launches the restricted profile
* activity.
*
* @param context used to get the appropriate string.
* @return the description string that should be used for an action that launches the restricted
* profile activity.
*/
public static String getActionDescription(Context context) {
return context.getString(isRestrictedProfileInEffect(context) ? R.string.on : R.string.off);
}
public static boolean isRestrictedProfileInEffect(Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
return userInfo.isRestricted();
}
/* package */ static void switchUserNow(int userId) {
try {
ActivityManagerNative.getDefault().switchUser(userId);
} catch (RemoteException re) {
Log.e(TAG, "Caught exception while switching user! ", re);
}
}
/* package */ static int getIconResource() {
return R.drawable.ic_settings_restricted_profile;
}
/* package */ static UserInfo findRestrictedUser(UserManager userManager) {
for (UserInfo userInfo : userManager.getUsers()) {
if (userInfo.isRestricted()) {
return userInfo;
}
}
return null;
}
// RestrictedProfilePinDialogFragment.Callback methods
@Override
public void saveLockPassword(String pin, int quality) {
new LockPatternUtils(getActivity()).saveLockPassword(pin, null, quality,
UserHandle.myUserId());
}
@Override
public boolean checkPassword(String password, int userId) {
try {
return getLockSettings().checkPassword(password, userId).getResponseCode()
== VerifyCredentialResponse.RESPONSE_OK;
} catch (final RemoteException e) {
// ignore
}
return false;
}
@Override
public boolean hasLockscreenSecurity() {
return RestrictedProfileDialogFragment.hasLockscreenSecurity(
new LockPatternUtils(getActivity()));
}
@Override
public void pinFragmentDone(boolean success) {
switch (mPinMode) {
case PIN_MODE_CHOOSE_LOCKSCREEN:
if (success) {
addRestrictedUser();
}
break;
case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
if (success) {
switchUserNow(UserHandle.USER_OWNER);
getActivity().finish();
}
break;
case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
// do nothing
break;
case PIN_MODE_RESTRICTED_PROFILE_DELETE:
if (success) {
removeRestrictedUser();
new LockPatternUtils(getActivity()).clearLock(UserHandle.myUserId());
}
break;
}
}
private ILockSettings getLockSettings() {
if (mLockSettingsService == null) {
mLockSettingsService = ILockSettings.Stub.asInterface(
ServiceManager.getService("lock_settings"));
}
return mLockSettingsService;
}
private ArrayList<Action> getMainMenuActions() {
ArrayList<Action> actions = new ArrayList<>();
if (mRestrictedUserInfo != null) {
if (mIsOwner) {
actions.add(new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_SWITCH_TO)
.title(getString(R.string.restricted_profile_switch_to))
.build());
actions.add(new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_CONFIG)
.title(getString(R.string.restricted_profile_configure_title))
.build());
actions.add(new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_DELETE)
.title(getString(R.string.restricted_profile_delete_title))
.build());
} else {
actions.add(new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_SWITCH_OUT)
.title(getString(R.string.restricted_profile_switch_out))
.build());
}
} else {
actions.add(new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_CREATE)
.title(getString(R.string.restricted_profile_configure_title))
.build());
}
return actions;
}
private ArrayList<Action> getConfigActions() {
ArrayList<Action> actions = new ArrayList<>();
actions.add(new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_CHANGE_PASSWORD)
.title(getString(R.string.restricted_profile_change_password_title))
.build());
actions.add(mConfigAppsAction);
return actions;
}
private Action createConfigAppsAction(int allowedApps) {
String description = allowedApps >= 0 ? getResources().getQuantityString(
R.plurals.restricted_profile_configure_apps_description, allowedApps, allowedApps)
: getString(R.string.restricted_profile_configure_apps_description_loading);
return new Action.Builder()
.key(ACTION_RESTRICTED_PROFILE_CONFIG_APPS)
.title(getString(R.string.restricted_profile_configure_apps_title))
.description(description)
.build();
}
private static boolean hasLockscreenSecurity(LockPatternUtils lpu) {
return lpu.isLockPasswordEnabled(UserHandle.myUserId())
|| lpu.isLockPatternEnabled(UserHandle.myUserId());
}
private void launchChooseLockscreen() {
if (getFragmentManager().findFragmentByTag(PinDialogFragment.DIALOG_TAG) != null) {
return;
}
mPinMode = PIN_MODE_CHOOSE_LOCKSCREEN;
RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
RestrictedProfilePinDialogFragment.newInstance(
PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN);
restrictedProfilePinDialogFragment.setTargetFragment(this, 0);
restrictedProfilePinDialogFragment.show(getFragmentManager(), PinDialogFragment.DIALOG_TAG);
}
private void removeRestrictedUser() {
mHandler.post(new Runnable() {
@Override
public void run() {
mUserManager.removeUser(mRestrictedUserInfo.id);
// pop confirm dialog
mRestrictedUserInfo = null;
UserSwitchListenerService.updateLaunchPoint(getActivity(), false);
mMainMenuDialogFragment.setActions(getMainMenuActions());
getFragmentManager().popBackStack();
}
});
}
private Bitmap createBitmapFromDrawable(int resId) {
Drawable icon = getActivity().getDrawable(resId);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
icon.draw(new Canvas(bitmap));
return bitmap;
}
private void addRestrictedUser() {
if (AsyncTask.Status.PENDING == mAddUserAsyncTask.getStatus()) {
mAddUserAsyncTask.execute((Void[]) null);
}
}
}