• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
getDisablePinUntil(Context context)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          */
setDisablePinUntil(Context context, long timeMillis)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 
RestrictedProfilePinDialogFragment(int type, ResultListener listener, LockPatternUtils lpu, ILockSettings iLockSettings)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
getPinDisabledUntil()103         public long getPinDisabledUntil() {
104             return getDisablePinUntil(getActivity());
105         }
106 
107         @Override
setPinDisabledUntil(long retryDisableTimeout)108         public void setPinDisabledUntil(long retryDisableTimeout) {
109             setDisablePinUntil(getActivity(), retryDisableTimeout);
110         }
111 
112         @Override
setPin(String pin)113         public void setPin(String pin) {
114             mLpu.saveLockPassword(pin, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
115         }
116 
117         @Override
isPinCorrect(String pin)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
isPinSet()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      */
getActionDescription(Context context)162     public static String getActionDescription(Context context) {
163         return context.getString(isRestrictedProfileInEffect(context) ? R.string.on : R.string.off);
164     }
165 
isRestrictedProfileInEffect(Context context)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 
switchUserNow(int userId)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 
getIconResource()182     static int getIconResource() {
183         return R.drawable.ic_settings_restricted_profile;
184     }
185 
findRestrictedUser(UserManager userManager)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
onCreate(Bundle savedInstanceState)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
onResume()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
onPackageEnableChanged(String packageName, boolean enabled)282     public void onPackageEnableChanged(String packageName, boolean enabled) {
283     }
284 
285     @Override
onActionsLoaded(ArrayList<Action> actions)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
onActionClicked(Action action)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 
getLockSettings()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 
getMainMenuActions()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 
getConfigActions()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 
createConfigAppsAction(int allowedApps)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 
hasLockscreenSecurity(LockPatternUtils lpu)436     private static boolean hasLockscreenSecurity(LockPatternUtils lpu) {
437         return lpu.isLockPasswordEnabled() || lpu.isLockPatternEnabled();
438     }
439 
launchChooseLockscreen()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 
removeRestrictedUser()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 
createBitmapFromDrawable(int resId)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 
addRestrictedUser()479     private void addRestrictedUser() {
480         if (AsyncTask.Status.PENDING == mAddUserAsyncTask.getStatus()) {
481             mAddUserAsyncTask.execute((Void[]) null);
482         }
483     }
484 }
485