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