• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.system;
18 
19 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
20 
21 import android.accounts.AccountManager;
22 import android.annotation.SuppressLint;
23 import android.app.admin.DevicePolicyManager;
24 import android.app.tvsettings.TvSettingsEnums;
25 import android.content.BroadcastReceiver;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.pm.UserInfo;
31 import android.graphics.Bitmap;
32 import android.graphics.Canvas;
33 import android.graphics.drawable.Drawable;
34 import android.os.AsyncTask;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.HandlerThread;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.text.TextUtils;
41 import android.util.Log;
42 
43 import androidx.annotation.DrawableRes;
44 import androidx.annotation.IntDef;
45 import androidx.annotation.Keep;
46 import androidx.fragment.app.Fragment;
47 import androidx.leanback.preference.LeanbackSettingsFragmentCompat;
48 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
49 import androidx.preference.Preference;
50 import androidx.preference.PreferenceGroup;
51 
52 import com.android.tv.settings.R;
53 import com.android.tv.settings.SettingsPreferenceFragment;
54 import com.android.tv.settings.dialog.PinDialogFragment;
55 import com.android.tv.settings.users.AppRestrictionsFragment;
56 import com.android.tv.settings.users.RestrictedProfileModel;
57 import com.android.tv.settings.users.RestrictedProfilePinDialogFragment;
58 import com.android.tv.settings.users.RestrictedProfilePinStorage;
59 import com.android.tv.settings.users.UserSwitchListenerService;
60 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
61 
62 import java.lang.annotation.Retention;
63 import java.lang.annotation.RetentionPolicy;
64 import java.util.List;
65 
66 /**
67  * The security settings screen in Tv settings.
68  */
69 @Keep
70 public class SecurityFragment extends SettingsPreferenceFragment
71         implements PinDialogFragment.ResultListener {
72 
73     private static final String TAG = "SecurityFragment";
74 
75     private static final String KEY_UNKNOWN_SOURCES = "unknown_sources";
76     private static final String KEY_RESTRICTED_PROFILE_GROUP = "restricted_profile_group";
77     private static final String KEY_RESTRICTED_PROFILE_ENTER = "restricted_profile_enter";
78     private static final String KEY_RESTRICTED_PROFILE_EXIT = "restricted_profile_exit";
79     private static final String KEY_RESTRICTED_PROFILE_APPS = "restricted_profile_apps";
80     private static final String KEY_RESTRICTED_PROFILE_PIN = "restricted_profile_pin";
81     private static final String KEY_RESTRICTED_PROFILE_CREATE = "restricted_profile_create";
82     private static final String KEY_RESTRICTED_PROFILE_DELETE = "restricted_profile_delete";
83     private static final String KEY_MANAGE_DEVICE_ADMIN = "manage_device_admin";
84     private static final String KEY_ENTERPRISE_PRIVACY = "enterprise_privacy";
85 
86     private static final String ACTION_RESTRICTED_PROFILE_CREATED =
87             "SecurityFragment.RESTRICTED_PROFILE_CREATED";
88     private static final String EXTRA_RESTRICTED_PROFILE_INFO =
89             "SecurityFragment.RESTRICTED_PROFILE_INFO";
90     private static final String SAVESTATE_CREATING_RESTRICTED_PROFILE =
91             "SecurityFragment.CREATING_RESTRICTED_PROFILE";
92 
93     @Retention(RetentionPolicy.SOURCE)
94     @IntDef({PIN_MODE_CHOOSE_LOCKSCREEN,
95             PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT,
96             PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD,
97             PIN_MODE_RESTRICTED_PROFILE_DELETE})
98     private @interface PinMode {}
99     private static final int PIN_MODE_CHOOSE_LOCKSCREEN = 1;
100     private static final int PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT = 2;
101     private static final int PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD = 3;
102     private static final int PIN_MODE_RESTRICTED_PROFILE_DELETE = 4;
103 
104     private Preference mUnknownSourcesPref;
105     private PreferenceGroup mRestrictedProfileGroup;
106     private Preference mRestrictedProfileEnterPref;
107     private Preference mRestrictedProfileExitPref;
108     private Preference mRestrictedProfileAppsPref;
109     private Preference mRestrictedProfilePinPref;
110     private Preference mRestrictedProfileCreatePref;
111     private Preference mRestrictedProfileDeletePref;
112 
113     private Preference mManageDeviceAdminPref;
114     private Preference mEnterprisePrivacyPref;
115 
116     private RestrictedProfileModel mRestrictedProfile;
117 
118     private boolean mCreatingRestrictedProfile;
119     private RestrictedProfilePinStorage mRestrictedProfilePinStorage;
120 
121     @SuppressLint("StaticFieldLeak")
122     private static CreateRestrictedProfileTask sCreateRestrictedProfileTask;
123     private final BroadcastReceiver mRestrictedProfileReceiver = new BroadcastReceiver() {
124         @Override
125         public void onReceive(Context context, Intent intent) {
126             UserInfo result = intent.getParcelableExtra(EXTRA_RESTRICTED_PROFILE_INFO);
127             if (isResumed()) {
128                 onRestrictedUserCreated(result);
129             }
130         }
131     };
132 
133     private Handler mUiThreadHandler;
134     private HandlerThread mBackgroundHandlerThread;
135     private Handler mBackgroundHandler;
136 
newInstance()137     public static SecurityFragment newInstance() {
138         return new SecurityFragment();
139     }
140 
141     @Override
onCreate(Bundle savedInstanceState)142     public void onCreate(Bundle savedInstanceState) {
143         mRestrictedProfile = new RestrictedProfileModel(getContext());
144 
145         super.onCreate(savedInstanceState);
146         mCreatingRestrictedProfile = savedInstanceState != null
147                 && savedInstanceState.getBoolean(SAVESTATE_CREATING_RESTRICTED_PROFILE);
148 
149         mUiThreadHandler = new Handler();
150         mBackgroundHandlerThread = new HandlerThread("SecurityFragmentBackgroundThread");
151         mBackgroundHandlerThread.start();
152         mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());
153     }
154 
155     @Override
onDestroy()156     public void onDestroy() {
157         mBackgroundHandler = null;
158         mBackgroundHandlerThread.quitSafely();
159         mBackgroundHandlerThread = null;
160         mUiThreadHandler = null;
161 
162         super.onDestroy();
163 
164         mRestrictedProfile = null;
165     }
166 
167     @Override
onResume()168     public void onResume() {
169         super.onResume();
170         refresh();
171         LocalBroadcastManager.getInstance(getActivity())
172                 .registerReceiver(mRestrictedProfileReceiver,
173                         new IntentFilter(ACTION_RESTRICTED_PROFILE_CREATED));
174         if (mCreatingRestrictedProfile) {
175             UserInfo userInfo = mRestrictedProfile.getUser();
176             if (userInfo != null) {
177                 onRestrictedUserCreated(userInfo);
178             }
179         }
180     }
181 
182     @Override
onPause()183     public void onPause() {
184         super.onPause();
185         LocalBroadcastManager.getInstance(getActivity())
186                 .unregisterReceiver(mRestrictedProfileReceiver);
187     }
188 
189     @Override
onAttach(Context context)190     public void onAttach(Context context) {
191         super.onAttach(context);
192         mRestrictedProfilePinStorage = RestrictedProfilePinStorage.newInstance(getContext());
193         mRestrictedProfilePinStorage.bind();
194     }
195 
196     @Override
onDetach()197     public void onDetach() {
198         mRestrictedProfilePinStorage.unbind();
199         mRestrictedProfilePinStorage = null;
200         super.onDetach();
201     }
202 
203     @Override
onSaveInstanceState(Bundle outState)204     public void onSaveInstanceState(Bundle outState) {
205         super.onSaveInstanceState(outState);
206         outState.putBoolean(SAVESTATE_CREATING_RESTRICTED_PROFILE, mCreatingRestrictedProfile);
207     }
208 
209     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)210     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
211         setPreferencesFromResource(R.xml.security, null);
212 
213         mUnknownSourcesPref = findPreference(KEY_UNKNOWN_SOURCES);
214         mRestrictedProfileGroup = (PreferenceGroup) findPreference(KEY_RESTRICTED_PROFILE_GROUP);
215         mRestrictedProfileEnterPref = findPreference(KEY_RESTRICTED_PROFILE_ENTER);
216         mRestrictedProfileExitPref = findPreference(KEY_RESTRICTED_PROFILE_EXIT);
217         mRestrictedProfileAppsPref = findPreference(KEY_RESTRICTED_PROFILE_APPS);
218         mRestrictedProfilePinPref = findPreference(KEY_RESTRICTED_PROFILE_PIN);
219         mRestrictedProfileCreatePref = findPreference(KEY_RESTRICTED_PROFILE_CREATE);
220         mRestrictedProfileDeletePref = findPreference(KEY_RESTRICTED_PROFILE_DELETE);
221 
222         mManageDeviceAdminPref = findPreference(KEY_MANAGE_DEVICE_ADMIN);
223         mEnterprisePrivacyPref = findPreference(KEY_ENTERPRISE_PRIVACY);
224     }
225 
refresh()226     private void refresh() {
227         if (mRestrictedProfile.isCurrentUser()) {
228             // We are in restricted profile
229             mUnknownSourcesPref.setVisible(false);
230 
231             mRestrictedProfileGroup.setVisible(true);
232             mRestrictedProfileEnterPref.setVisible(false);
233             mRestrictedProfileExitPref.setVisible(true);
234             mRestrictedProfileAppsPref.setVisible(false);
235             mRestrictedProfilePinPref.setVisible(false);
236             mRestrictedProfileCreatePref.setVisible(false);
237             mRestrictedProfileDeletePref.setVisible(false);
238         } else if (mRestrictedProfile.getUser() != null) {
239             // Not in restricted profile, but it exists
240             mUnknownSourcesPref.setVisible(true);
241 
242             mRestrictedProfileGroup.setVisible(true);
243             mRestrictedProfileEnterPref.setVisible(true);
244             mRestrictedProfileExitPref.setVisible(false);
245             mRestrictedProfileAppsPref.setVisible(true);
246             mRestrictedProfilePinPref.setVisible(true);
247             mRestrictedProfileCreatePref.setVisible(false);
248             mRestrictedProfileDeletePref.setVisible(true);
249 
250             AppRestrictionsFragment.prepareArgs(mRestrictedProfileAppsPref.getExtras(),
251                     mRestrictedProfile.getUser().id, false);
252         } else if (UserManager.supportsMultipleUsers()) {
253             // Not in restricted profile, and it doesn't exist
254             mUnknownSourcesPref.setVisible(true);
255 
256             mRestrictedProfileGroup.setVisible(true);
257             mRestrictedProfileEnterPref.setVisible(false);
258             mRestrictedProfileExitPref.setVisible(false);
259             mRestrictedProfileAppsPref.setVisible(false);
260             mRestrictedProfilePinPref.setVisible(false);
261             mRestrictedProfileCreatePref.setVisible(true);
262             mRestrictedProfileDeletePref.setVisible(false);
263         } else {
264             // Not in restricted profile, and can't create one either
265             mUnknownSourcesPref.setVisible(true);
266 
267             mRestrictedProfileGroup.setVisible(false);
268             mRestrictedProfileEnterPref.setVisible(false);
269             mRestrictedProfileExitPref.setVisible(false);
270             mRestrictedProfileAppsPref.setVisible(false);
271             mRestrictedProfilePinPref.setVisible(false);
272             mRestrictedProfileCreatePref.setVisible(false);
273             mRestrictedProfileDeletePref.setVisible(false);
274         }
275 
276         mRestrictedProfileCreatePref.setEnabled(sCreateRestrictedProfileTask == null);
277 
278         mUnknownSourcesPref.setEnabled(!isUnknownSourcesBlocked());
279 
280         mManageDeviceAdminPref.setVisible(hasActiveAdmins());
281         mEnterprisePrivacyPref.setVisible(isDeviceManaged());
282     }
283 
284     @Override
onPreferenceTreeClick(Preference preference)285     public boolean onPreferenceTreeClick(Preference preference) {
286         final String key = preference.getKey();
287         if (TextUtils.isEmpty(key)) {
288             return super.onPreferenceTreeClick(preference);
289         }
290         switch (key) {
291             case KEY_RESTRICTED_PROFILE_ENTER:
292                 logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_ENTER_PROFILE);
293                 if (mRestrictedProfile.enterUser()) {
294                     getActivity().finish();
295                 }
296                 return true;
297             case KEY_RESTRICTED_PROFILE_EXIT:
298                 logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_EXIT_PROFILE);
299                 launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT);
300                 return true;
301             case KEY_RESTRICTED_PROFILE_PIN:
302                 logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_PROFILE_CHANGE_PIN);
303                 launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD);
304                 return true;
305             case KEY_RESTRICTED_PROFILE_CREATE:
306                 logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_CREATE_PROFILE);
307                 createRestrictedProfile();
308                 return true;
309             case KEY_RESTRICTED_PROFILE_DELETE:
310                 logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_DELETE_PROFILE);
311                 launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_DELETE);
312                 return true;
313         }
314         return super.onPreferenceTreeClick(preference);
315     }
316 
createRestrictedProfile()317     private void createRestrictedProfile() {
318         mBackgroundHandler.post(() -> {
319             boolean pinIsSet = mRestrictedProfilePinStorage.isPinSet();
320 
321             mUiThreadHandler.post(() -> {
322                 if (pinIsSet) {
323                     addRestrictedUser();
324                 } else {
325                     launchPinDialog(PIN_MODE_CHOOSE_LOCKSCREEN);
326                 }
327             });
328         });
329     }
330 
isUnknownSourcesBlocked()331     private boolean isUnknownSourcesBlocked() {
332         final UserManager um = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
333         return um.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
334     }
335 
isDeviceManaged()336     private boolean isDeviceManaged() {
337         final DevicePolicyManager devicePolicyManager = getContext().getSystemService(
338                 DevicePolicyManager.class);
339         return devicePolicyManager.isDeviceManaged();
340     }
341 
hasActiveAdmins()342     private boolean hasActiveAdmins() {
343         final DevicePolicyManager devicePolicyManager = getContext().getSystemService(
344                 DevicePolicyManager.class);
345         final List<ComponentName> admins = devicePolicyManager.getActiveAdmins();
346         return (admins != null && !admins.isEmpty());
347     }
348 
launchPinDialog(@inMode int pinMode)349     private void launchPinDialog(@PinMode int pinMode) {
350         @PinDialogFragment.PinDialogType
351         int pinDialogMode;
352 
353         switch (pinMode) {
354             case PIN_MODE_CHOOSE_LOCKSCREEN:
355                 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN;
356                 break;
357             case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
358                 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN;
359                 break;
360             case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
361                 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN;
362                 break;
363             case PIN_MODE_RESTRICTED_PROFILE_DELETE:
364                 pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_DELETE_PIN;
365                 break;
366             default:
367                 throw new IllegalArgumentException("Unknown pin mode: " + pinMode);
368         }
369 
370         RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
371                 RestrictedProfilePinDialogFragment.newInstance(pinDialogMode);
372         restrictedProfilePinDialogFragment.setTargetFragment(this, pinMode);
373         restrictedProfilePinDialogFragment.show(getFragmentManager(),
374                 PinDialogFragment.DIALOG_TAG);
375     }
376 
377     @Override
pinFragmentDone(int requestCode, boolean success)378     public void pinFragmentDone(int requestCode, boolean success) {
379         if (!success) {
380             Log.d(TAG, "Request " + requestCode + " unsuccessful.");
381             return;
382         }
383 
384         switch (requestCode) {
385             case PIN_MODE_CHOOSE_LOCKSCREEN:
386                 addRestrictedUser();
387                 break;
388             case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
389                 mRestrictedProfile.exitUser();
390                 getActivity().finish();
391                 break;
392             case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
393                 // do nothing
394                 break;
395             case PIN_MODE_RESTRICTED_PROFILE_DELETE:
396                 mUiThreadHandler.post(() -> {
397                     mRestrictedProfile.removeUser();
398                     UserSwitchListenerService.updateLaunchPoint(getActivity(), false);
399                     refresh();
400                 });
401                 break;
402             default:
403                 Log.d(TAG, "Pin request code not recognised: " + requestCode);
404         }
405     }
406 
addRestrictedUser()407     private void addRestrictedUser() {
408         if (sCreateRestrictedProfileTask == null) {
409             sCreateRestrictedProfileTask = new CreateRestrictedProfileTask(getContext());
410             sCreateRestrictedProfileTask.execute();
411             mCreatingRestrictedProfile = true;
412         }
413         refresh();
414     }
415 
416     /**
417       * Called by other Fragments to decide whether to show or hide profile-related views.
418       */
isRestrictedProfileInEffect(Context context)419     public static boolean isRestrictedProfileInEffect(Context context) {
420         return new RestrictedProfileModel(context).isCurrentUser();
421     }
422 
onRestrictedUserCreated(UserInfo result)423     private void onRestrictedUserCreated(UserInfo result) {
424         int userId = result.id;
425         if (result.isRestricted()
426                 && result.restrictedProfileParentId == UserHandle.myUserId()) {
427             final AppRestrictionsFragment restrictionsFragment =
428                     AppRestrictionsFragment.newInstance(userId, true);
429             final Fragment settingsFragment = getCallbackFragment();
430             if (settingsFragment instanceof LeanbackSettingsFragmentCompat) {
431                 ((LeanbackSettingsFragmentCompat) settingsFragment)
432                         .startPreferenceFragment(restrictionsFragment);
433             } else if (settingsFragment instanceof TwoPanelSettingsFragment) {
434                 ((TwoPanelSettingsFragment) settingsFragment)
435                         .startPreferenceFragment(restrictionsFragment);
436             } else {
437                 throw new IllegalStateException("Didn't find fragment of expected type: "
438                         + settingsFragment);
439             }
440         }
441         mCreatingRestrictedProfile = false;
442         refresh();
443     }
444 
445     private static class CreateRestrictedProfileTask extends AsyncTask<Void, Void, UserInfo> {
446         private final Context mContext;
447         private final UserManager mUserManager;
448 
CreateRestrictedProfileTask(Context context)449         CreateRestrictedProfileTask(Context context) {
450             mContext = context.getApplicationContext();
451             mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
452         }
453 
454         @Override
doInBackground(Void... params)455         protected UserInfo doInBackground(Void... params) {
456             UserInfo restrictedUserInfo = mUserManager.createProfileForUser(
457                     mContext.getString(R.string.user_new_profile_name),
458                     UserManager.USER_TYPE_FULL_RESTRICTED, /* flags */ 0, UserHandle.myUserId());
459             if (restrictedUserInfo == null) {
460                 final UserInfo existingUserInfo = new RestrictedProfileModel(mContext).getUser();
461                 if (existingUserInfo == null) {
462                     Log.wtf(TAG, "Got back a null user handle!");
463                 }
464                 return existingUserInfo;
465             }
466             int userId = restrictedUserInfo.id;
467             UserHandle user = new UserHandle(userId);
468             mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
469             Bitmap bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default);
470             mUserManager.setUserIcon(userId, bitmap);
471             // Add shared accounts
472             AccountManager.get(mContext).addSharedAccountsFromParentUser(
473                     UserHandle.of(UserHandle.myUserId()), user);
474             return restrictedUserInfo;
475         }
476 
477         @Override
onPostExecute(UserInfo result)478         protected void onPostExecute(UserInfo result) {
479             sCreateRestrictedProfileTask = null;
480             if (result == null) {
481                 return;
482             }
483             UserSwitchListenerService.updateLaunchPoint(mContext, true);
484             LocalBroadcastManager.getInstance(mContext).sendBroadcast(
485                     new Intent(ACTION_RESTRICTED_PROFILE_CREATED)
486                             .putExtra(EXTRA_RESTRICTED_PROFILE_INFO, result));
487         }
488 
createBitmapFromDrawable(@rawableRes int resId)489         private Bitmap createBitmapFromDrawable(@DrawableRes int resId) {
490             Drawable icon = mContext.getDrawable(resId);
491             if (icon == null) {
492                 throw new IllegalArgumentException("Drawable is missing!");
493             }
494             icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
495             Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
496                     Bitmap.Config.ARGB_8888);
497             icon.draw(new Canvas(bitmap));
498             return bitmap;
499         }
500     }
501 
502     @Override
getPageId()503     protected int getPageId() {
504         return TvSettingsEnums.APPS_SECURITY_RESTRICTIONS;
505     }
506 }
507