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