• 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 android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.IPackageManager;
25 import android.content.pm.PackageManager;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.util.Log;
33 
34 import com.android.tv.settings.R;
35 import com.android.tv.settings.dialog.DialogFragment;
36 import com.android.tv.settings.dialog.DialogFragment.Action;
37 
38 import java.util.ArrayList;
39 import java.util.HashMap;
40 import java.util.Map;
41 
42 /**
43  * DialogFragment that configures the app restrictions for a given user.
44  */
45 public class UserAppRestrictionsDialogFragment extends DialogFragment implements Action.Listener,
46         AppLoadingTask.Listener {
47 
48     private static final boolean DEBUG = false;
49     private static final String TAG = "RestrictedProfile";
50 
51     /** Key for extra passed in from calling fragment for the userId of the user being edited */
52     public static final String EXTRA_USER_ID = "user_id";
53 
54     /** Key for extra passed in from calling fragment to indicate if this is a newly created user */
55     public static final String EXTRA_NEW_USER = "new_user";
56 
57     private static final String EXTRA_CONTENT_TITLE = "title";
58     private static final String EXTRA_CONTENT_BREADCRUMB = "breadcrumb";
59     private static final String EXTRA_CONTENT_DESCRIPTION = "description";
60     private static final String EXTRA_CONTENT_ICON_RESOURCE_ID = "iconResourceId";
61     private static final String EXTRA_CONTENT_ICON_URI = "iconUri";
62     private static final String EXTRA_CONTENT_ICON_BITMAP = "iconBitmap";
63     private static final String EXTRA_CONTENT_ICON_BACKGROUND = "iconBackground";
64 
65     private static final String EXTRA_PACKAGE_NAME = "packageName";
66     private static final String EXTRA_CAN_CONFIGURE_RESTRICTIONS = "canConfigureRestrictions";
67     private static final String EXTRA_CAN_SEE_RESTRICTED_ACCOUNTS = "canSeeRestrictedAccounts";
68     private static final String EXTRA_IS_ALLOWED = "isAllowed";
69     private static final String EXTRA_CAN_BE_ENABLED_DISABLED = "canBeEnabledDisabled";
70     private static final String EXTRA_CONTROLLING_APP = "controllingApp";
71 
72     private static final String ACTION_ALLOW = "allow";
73     private static final String ACTION_DISALLOW = "disallow";
74     private static final String ACTION_CONFIGURE = "configure";
75     private static final String ACTION_CUSTOMIZE_RESTRICTIONS = "customizeRestriction";
76 
77     private static final int CHECK_SET_ID = 1;
78 
newInstance(Context context, int userId, boolean newUser)79     public static UserAppRestrictionsDialogFragment newInstance(Context context, int userId,
80             boolean newUser) {
81         UserAppRestrictionsDialogFragment fragment = new UserAppRestrictionsDialogFragment();
82         Bundle args = new Bundle();
83         args.putString(EXTRA_CONTENT_TITLE,
84                 context.getString(R.string.restricted_profile_configure_apps_title));
85         args.putInt(EXTRA_CONTENT_ICON_RESOURCE_ID, R.drawable.ic_settings_launcher_icon);
86         args.putInt(EXTRA_CONTENT_ICON_BACKGROUND,
87                 context.getResources().getColor(R.color.icon_background));
88         args.putInt(EXTRA_USER_ID, userId);
89         args.putBoolean(EXTRA_NEW_USER, newUser);
90         fragment.setArguments(args);
91         return fragment;
92     }
93 
94     private UserManager mUserManager;
95     private IPackageManager mIPm;
96     private AppLoadingTask mAppLoadingTask;
97     private final HashMap<String, Boolean> mSelectedPackages = new HashMap<>();
98     private UserHandle mUser;
99     private boolean mNewUser;
100     private boolean mAppListChanged;
101     private AppRestrictionsManager mAppRestrictionsManager;
102 
103     private final BroadcastReceiver mUserBackgrounding = new BroadcastReceiver() {
104         @Override
105         public void onReceive(Context context, Intent intent) {
106             // Update the user's app selection right away without waiting for a pause
107             // onPause() might come in too late, causing apps to disappear after broadcasts
108             // have been scheduled during user startup.
109             if (mAppListChanged) {
110                 if (DEBUG) {
111                     Log.d(TAG, "User backgrounding, update app list");
112                 }
113                 applyUserAppsStates(mSelectedPackages, getActions(), mIPm, mUser.getIdentifier());
114                 if (DEBUG) {
115                     Log.d(TAG, "User backgrounding, done updating app list");
116                 }
117             }
118         }
119     };
120     private final BroadcastReceiver mPackageObserver = new BroadcastReceiver() {
121         @Override
122         public void onReceive(Context context, Intent intent) {
123             onPackageChanged(intent);
124         }
125 
126         private void onPackageChanged(Intent intent) {
127             String action = intent.getAction();
128             String packageName = intent.getData().getSchemeSpecificPart();
129             // Package added, check if the preference needs to be enabled
130             ArrayList<Action> matchingActions = findActionsWithPackageName(getActions(),
131                     packageName);
132             for (Action matchingAction : matchingActions) {
133                 if ((Intent.ACTION_PACKAGE_ADDED.equals(action) && matchingAction.isChecked()) || (
134                         Intent.ACTION_PACKAGE_REMOVED.equals(action)
135                         && !matchingAction.isChecked())) {
136                     matchingAction.setEnabled(true);
137                 }
138             }
139         }
140     };
141 
142     @Override
onCreate(Bundle icicle)143     public void onCreate(Bundle icicle) {
144         super.onCreate(icicle);
145         mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
146         setActions(new ArrayList<Action>());
147         setListener(this);
148         if (icicle != null) {
149             mUser = new UserHandle(icicle.getInt(EXTRA_USER_ID));
150         } else {
151             Bundle args = getArguments();
152             if (args != null) {
153                 if (args.containsKey(EXTRA_USER_ID)) {
154                     mUser = new UserHandle(args.getInt(EXTRA_USER_ID));
155                 }
156                 mNewUser = args.getBoolean(EXTRA_NEW_USER, false);
157             }
158         }
159 
160         if (mUser == null) {
161             mUser = android.os.Process.myUserHandle();
162         }
163 
164         mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
165     }
166 
167     @Override
onResume()168     public void onResume() {
169         super.onResume();
170 
171         getActivity().registerReceiver(mUserBackgrounding,
172                 new IntentFilter(Intent.ACTION_USER_BACKGROUND));
173         IntentFilter packageFilter = new IntentFilter();
174         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
175         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
176         packageFilter.addDataScheme("package");
177         getActivity().registerReceiver(mPackageObserver, packageFilter);
178 
179         if (mAppLoadingTask == null) {
180             mAppListChanged = false;
181             mAppLoadingTask = new AppLoadingTask(getActivity(), mUser.getIdentifier(), mNewUser,
182                     mIPm, this);
183             mAppLoadingTask.execute((Void[]) null);
184         }
185     }
186 
187     @Override
onPause()188     public void onPause() {
189         super.onPause();
190         mNewUser = false;
191         getActivity().unregisterReceiver(mUserBackgrounding);
192         getActivity().unregisterReceiver(mPackageObserver);
193         if (mAppListChanged) {
194             new Thread() {
195                 public void run() {
196                     applyUserAppsStates(mSelectedPackages, getActions(), mIPm,
197                             mUser.getIdentifier());
198                 }
199             }.start();
200         }
201     }
202 
203     @Override
onActionClicked(Action action)204     public void onActionClicked(Action action) {
205         String packageName = action.getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
206         if (ACTION_CONFIGURE.equals(action.getKey())) {
207             boolean isAllowed = action.getIntent().getBooleanExtra(EXTRA_IS_ALLOWED, false);
208             boolean canBeEnabledDisabled = action.getIntent().getBooleanExtra(
209                     EXTRA_CAN_BE_ENABLED_DISABLED, false);
210             String controllingActivity = action.getIntent().getStringExtra(EXTRA_CONTROLLING_APP);
211             final ArrayList<Action> initialAllowDisallowActions = new ArrayList<>();
212             if (controllingActivity != null) {
213                 initialAllowDisallowActions.add(new Action.Builder()
214                         .title(getString(isAllowed ? R.string.restricted_profile_allowed
215                                 : R.string.restricted_profile_not_allowed))
216                         .description(getString(R.string.user_restrictions_controlled_by,
217                                 controllingActivity))
218                         .checked(isAllowed)
219                         .infoOnly(true)
220                         .build());
221             } else if (!canBeEnabledDisabled) {
222                 initialAllowDisallowActions.add(new Action.Builder()
223                         .title(getString(isAllowed ? R.string.restricted_profile_allowed
224                                 : R.string.restricted_profile_not_allowed))
225                         .checked(isAllowed)
226                         .infoOnly(true)
227                         .build());
228             } else {
229                 boolean canSeeRestrictedAccounts = action.getIntent().getBooleanExtra(
230                         EXTRA_CAN_SEE_RESTRICTED_ACCOUNTS, true);
231                 initialAllowDisallowActions.add(new Action.Builder().key(ACTION_DISALLOW)
232                         .title(getString(R.string.restricted_profile_not_allowed))
233                         .intent(action.getIntent())
234                         .checked(!isAllowed)
235                         .checkSetId(CHECK_SET_ID).build());
236                 initialAllowDisallowActions.add(new Action.Builder().key(ACTION_ALLOW)
237                         .title(getString(R.string.restricted_profile_allowed))
238                         .description(canSeeRestrictedAccounts ? getString(
239                                 R.string.app_sees_restricted_accounts)
240                                 : null)
241                         .intent(action.getIntent())
242                         .checkSetId(CHECK_SET_ID)
243                         .checked(isAllowed)
244                         .build());
245             }
246 
247             final DialogFragment dialogFragment = new DialogFragment.Builder()
248                     .title(action.getTitle()).iconUri(action.getIconUri())
249                     .actions(initialAllowDisallowActions).build();
250 
251             boolean canConfigureRestrictions = action.getIntent().getBooleanExtra(
252                     EXTRA_CAN_CONFIGURE_RESTRICTIONS, false);
253             if (canConfigureRestrictions) {
254                 mAppRestrictionsManager = new AppRestrictionsManager(this, mUser,
255                         mUserManager, packageName, new AppRestrictionsManager.Listener() {
256                             @Override
257                             public void onRestrictionActionsLoaded(String packageName,
258                                     ArrayList<Action> restrictionActions) {
259                                 ArrayList<Action> oldActions = dialogFragment.getActions();
260                                 ArrayList<Action> newActions = new ArrayList<>();
261                                 if (oldActions != null && oldActions.size()
262                                         >= initialAllowDisallowActions.size()) {
263                                     for (int i = 0, size = initialAllowDisallowActions.size();
264                                             i < size; i++) {
265                                         newActions.add(oldActions.get(i));
266                                     }
267                                 } else {
268                                     newActions.addAll(initialAllowDisallowActions);
269                                 }
270                                 newActions.addAll(restrictionActions);
271                                 dialogFragment.setActions(newActions);
272                             }
273                         });
274                 mAppRestrictionsManager.loadRestrictionActions();
275             }
276 
277             dialogFragment.setListener(this);
278             DialogFragment.add(getFragmentManager(), dialogFragment);
279         } else if (ACTION_ALLOW.equals(action.getKey())) {
280             setEnabled(packageName, true);
281             getFragmentManager().popBackStack();
282         } else if (ACTION_DISALLOW.equals(action.getKey())) {
283             setEnabled(packageName, false);
284             getFragmentManager().popBackStack();
285         } else if (mAppRestrictionsManager != null) {
286             mAppRestrictionsManager.onActionClicked(action);
287         }
288     }
289 
290     @Override
onActivityResult(int requestCode, int resultCode, Intent data)291     public void onActivityResult(int requestCode, int resultCode, Intent data) {
292         if (mAppRestrictionsManager != null) {
293             mAppRestrictionsManager.onActivityResult(requestCode, resultCode, data);
294         }
295     }
296 
setEnabled(String packageName, boolean enabled)297     private void setEnabled(String packageName, boolean enabled) {
298         onPackageEnableChanged(packageName, enabled);
299         for (Action action : getActions()) {
300             String actionPackageName = action.getIntent().getStringExtra(EXTRA_PACKAGE_NAME);
301             if (actionPackageName.equals(packageName)) {
302                 action.setChecked(enabled);
303                 action.setDescription(getString(enabled ? R.string.restricted_profile_allowed
304                         : R.string.restricted_profile_not_allowed));
305                 action.getIntent().putExtra(EXTRA_IS_ALLOWED, enabled);
306             }
307         }
308         onActionsLoaded(getActions());
309     }
310 
311     @Override
onPackageEnableChanged(String packageName, boolean enabled)312     public void onPackageEnableChanged(String packageName, boolean enabled) {
313         mSelectedPackages.put(packageName, enabled);
314         mAppListChanged = true;
315         if (getActivity() instanceof AppLoadingTask.Listener) {
316             ((AppLoadingTask.Listener) getActivity()).onPackageEnableChanged(packageName, enabled);
317         }
318     }
319 
320     @Override
onActionsLoaded(ArrayList<Action> actions)321     public void onActionsLoaded(ArrayList<Action> actions) {
322         setActions(actions);
323         if (getActivity() instanceof AppLoadingTask.Listener) {
324             ((AppLoadingTask.Listener) getActivity()).onActionsLoaded(actions);
325         }
326     }
327 
createAction(Context context, String packageName, String title, Uri iconUri, boolean canBeEnabledDisabled, boolean isAllowed, boolean hasCustomizableRestrictions, boolean canSeeRestrictedAccounts, boolean availableForRestrictedProfile, String controllingActivity)328     static Action createAction(Context context, String packageName, String title, Uri iconUri,
329             boolean canBeEnabledDisabled, boolean isAllowed, boolean hasCustomizableRestrictions,
330             boolean canSeeRestrictedAccounts, boolean availableForRestrictedProfile,
331             String controllingActivity) {
332         String description = context.getString(
333                 availableForRestrictedProfile ? isAllowed ? R.string.restricted_profile_allowed
334                         : R.string.restricted_profile_not_allowed
335                         : R.string.app_not_supported_in_limited);
336 
337         Intent intent = new Intent().putExtra(EXTRA_IS_ALLOWED, isAllowed)
338                 .putExtra(EXTRA_CAN_CONFIGURE_RESTRICTIONS,
339                         (hasCustomizableRestrictions && (controllingActivity == null)))
340                 .putExtra(EXTRA_CAN_BE_ENABLED_DISABLED, canBeEnabledDisabled)
341                 .putExtra(EXTRA_PACKAGE_NAME, packageName)
342                 .putExtra(EXTRA_CONTROLLING_APP, controllingActivity)
343                 .putExtra(EXTRA_CAN_SEE_RESTRICTED_ACCOUNTS, canSeeRestrictedAccounts);
344 
345         if (DEBUG) {
346             Log.d(TAG, "Icon uri: " + (iconUri != null ? iconUri.toString() : "null"));
347         }
348         return new Action.Builder()
349                 .key(ACTION_CONFIGURE)
350                 .title(title)
351                 .description(description)
352                 .enabled(availableForRestrictedProfile)
353                 .iconUri(iconUri)
354                 .checked(isAllowed)
355                 .intent(intent)
356                 .build();
357     }
358 
applyUserAppsStates(HashMap<String, Boolean> selectedPackages, ArrayList<Action> actions, IPackageManager ipm, int userId)359     static void applyUserAppsStates(HashMap<String, Boolean> selectedPackages,
360             ArrayList<Action> actions, IPackageManager ipm, int userId) {
361         for (Map.Entry<String, Boolean> entry : selectedPackages.entrySet()) {
362             String packageName = entry.getKey();
363             boolean enabled = entry.getValue();
364             if (applyUserAppState(ipm, userId, packageName, enabled)) {
365                 disableActionForPackage(actions, packageName);
366             }
367         }
368     }
369 
applyUserAppState(IPackageManager ipm, int userId, String packageName, boolean enabled)370     static boolean applyUserAppState(IPackageManager ipm, int userId, String packageName,
371             boolean enabled) {
372         boolean disableActionForPackage = false;
373         if (enabled) {
374             // Enable selected apps
375             try {
376                 ApplicationInfo info = ipm.getApplicationInfo(packageName,
377                         PackageManager.GET_UNINSTALLED_PACKAGES, userId);
378                 if (info == null || !info.enabled
379                         || (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
380                     ipm.installExistingPackageAsUser(packageName, userId);
381                     if (DEBUG) {
382                         Log.d(TAG, "Installing " + packageName);
383                     }
384                 }
385                 if (info != null && (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0
386                         && (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
387                     disableActionForPackage = true;
388                     ipm.setApplicationHiddenSettingAsUser(packageName, false, userId);
389                     if (DEBUG) {
390                         Log.d(TAG, "Unhiding " + packageName);
391                     }
392                 }
393             } catch (RemoteException re) {
394                 Log.e(TAG, "Caught exception while installing " + packageName + "!", re);
395             }
396         } else {
397             // Blacklist all other apps, system or downloaded
398             try {
399                 ApplicationInfo info = ipm.getApplicationInfo(packageName, 0, userId);
400                 if (info != null) {
401                     ipm.deletePackageAsUser(packageName, null, userId,
402                             PackageManager.DELETE_SYSTEM_APP);
403                     if (DEBUG) {
404                         Log.d(TAG, "Uninstalling " + packageName);
405                     }
406                 }
407             } catch (RemoteException re) {
408                 Log.e(TAG, "Caught exception while uninstalling " + packageName + "!", re);
409             }
410         }
411         return disableActionForPackage;
412     }
413 
disableActionForPackage(ArrayList<Action> actions, String packageName)414     private static void disableActionForPackage(ArrayList<Action> actions, String packageName) {
415         ArrayList<Action> matchingActions = findActionsWithPackageName(actions, packageName);
416         for(Action matchingAction : matchingActions) {
417             matchingAction.setEnabled(false);
418         }
419     }
420 
findActionsWithPackageName(ArrayList<Action> actions, String packageName)421     private static ArrayList<Action> findActionsWithPackageName(ArrayList<Action> actions,
422             String packageName) {
423         ArrayList<Action> matchingActions = new ArrayList<>();
424         if (packageName != null) {
425             for (Action action : actions) {
426                 if (packageName.equals(action.getIntent().getStringExtra(EXTRA_PACKAGE_NAME))) {
427                     matchingActions.add(action);
428                 }
429             }
430         }
431         return matchingActions;
432     }
433 }
434