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.example.android.basicmanagedprofile;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.Fragment;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.ActivityNotFoundException;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageManager;
30 import android.os.Bundle;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.widget.Button;
36 import android.widget.CompoundButton;
37 import android.widget.ScrollView;
38 import android.widget.Switch;
39 import android.widget.TextView;
40 import android.widget.Toast;
41 
42 import static android.app.admin.DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT;
43 import static android.app.admin.DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED;
44 
45 /**
46  * Provides several functions that are available in a managed profile. This includes
47  * enabling/disabling other apps, setting app restrictions, enabling/disabling intent forwarding,
48  * and wiping out all the data in the profile.
49  */
50 public class BasicManagedProfileFragment extends Fragment
51         implements View.OnClickListener,
52         CompoundButton.OnCheckedChangeListener {
53 
54     /**
55      * Tag for logging.
56      */
57     private static final String TAG = "BasicManagedProfileFragment";
58 
59     /**
60      * Package name of calculator
61      */
62     private static final String PACKAGE_NAME_CALCULATOR = "com.android.calculator2";
63 
64     /**
65      * Package name of Chrome
66      */
67     private static final String PACKAGE_NAME_CHROME = "com.android.chrome";
68 
69     /**
70      * {@link Button} to remove this managed profile.
71      */
72     private Button mButtonRemoveProfile;
73 
74     /**
75      * Whether the calculator app is enabled in this profile
76      */
77     private boolean mCalculatorEnabled;
78 
79     /**
80      * Whether Chrome is enabled in this profile
81      */
82     private boolean mChromeEnabled;
83 
BasicManagedProfileFragment()84     public BasicManagedProfileFragment() {
85     }
86 
newInstance()87     public static BasicManagedProfileFragment newInstance() {
88         return new BasicManagedProfileFragment();
89     }
90 
91     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)92     public View onCreateView(LayoutInflater inflater, ViewGroup container,
93                              Bundle savedInstanceState) {
94         return inflater.inflate(R.layout.fragment_main, container, false);
95     }
96 
97     @Override
onAttach(Activity activity)98     public void onAttach(Activity activity) {
99         super.onAttach(activity);
100         // Retrieves whether the calculator app is enabled in this profile
101         mCalculatorEnabled = isApplicationEnabled(PACKAGE_NAME_CALCULATOR);
102         // Retrieves whether Chrome is enabled in this profile
103         mChromeEnabled = isApplicationEnabled(PACKAGE_NAME_CHROME);
104     }
105 
106     /**
107      * Checks if the application is available in this profile.
108      *
109      * @param packageName The package name
110      * @return True if the application is available in this profile.
111      */
isApplicationEnabled(String packageName)112     private boolean isApplicationEnabled(String packageName) {
113         Activity activity = getActivity();
114         PackageManager packageManager = activity.getPackageManager();
115         try {
116             ApplicationInfo applicationInfo = packageManager.getApplicationInfo(
117                     packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
118             // Return false if the app is not installed in this profile
119             if (0 == (applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)) {
120                 return false;
121             }
122             // Check if the app is not hidden in this profile
123             DevicePolicyManager devicePolicyManager =
124                     (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
125             return !devicePolicyManager.isApplicationHidden(
126                     BasicDeviceAdminReceiver.getComponentName(activity), packageName);
127         } catch (PackageManager.NameNotFoundException e) {
128             return false;
129         }
130     }
131 
132     @Override
onViewCreated(View view, Bundle savedInstanceState)133     public void onViewCreated(View view, Bundle savedInstanceState) {
134         // Bind event listeners and initial states
135         view.findViewById(R.id.set_chrome_restrictions).setOnClickListener(this);
136         view.findViewById(R.id.clear_chrome_restrictions).setOnClickListener(this);
137         view.findViewById(R.id.enable_forwarding).setOnClickListener(this);
138         view.findViewById(R.id.disable_forwarding).setOnClickListener(this);
139         view.findViewById(R.id.send_intent).setOnClickListener(this);
140         mButtonRemoveProfile = (Button) view.findViewById(R.id.remove_profile);
141         mButtonRemoveProfile.setOnClickListener(this);
142         Switch toggleCalculator = (Switch) view.findViewById(R.id.toggle_calculator);
143         toggleCalculator.setChecked(mCalculatorEnabled);
144         toggleCalculator.setOnCheckedChangeListener(this);
145         Switch toggleChrome = (Switch) view.findViewById(R.id.toggle_chrome);
146         toggleChrome.setChecked(mChromeEnabled);
147         toggleChrome.setOnCheckedChangeListener(this);
148     }
149 
150     @Override
onClick(View view)151     public void onClick(View view) {
152         switch (view.getId()) {
153             case R.id.set_chrome_restrictions: {
154                 setChromeRestrictions();
155                 break;
156             }
157             case R.id.clear_chrome_restrictions: {
158                 clearChromeRestrictions();
159                 break;
160             }
161             case R.id.enable_forwarding: {
162                 enableForwarding();
163                 break;
164             }
165             case R.id.disable_forwarding: {
166                 disableForwarding();
167                 break;
168             }
169             case R.id.send_intent: {
170                 sendIntent();
171                 break;
172             }
173             case R.id.remove_profile: {
174                 mButtonRemoveProfile.setEnabled(false);
175                 removeProfile();
176                 break;
177             }
178         }
179     }
180 
181     @Override
onCheckedChanged(CompoundButton compoundButton, boolean checked)182     public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
183         switch (compoundButton.getId()) {
184             case R.id.toggle_calculator: {
185                 setAppEnabled(PACKAGE_NAME_CALCULATOR, checked);
186                 mCalculatorEnabled = checked;
187                 break;
188             }
189             case R.id.toggle_chrome: {
190                 setAppEnabled(PACKAGE_NAME_CHROME, checked);
191                 mChromeEnabled = checked;
192                 break;
193             }
194         }
195     }
196 
197     /**
198      * Enables or disables the specified app in this profile.
199      *
200      * @param packageName The package name of the target app.
201      * @param enabled     Pass true to enable the app.
202      */
setAppEnabled(String packageName, boolean enabled)203     private void setAppEnabled(String packageName, boolean enabled) {
204         Activity activity = getActivity();
205         if (null == activity) {
206             return;
207         }
208         PackageManager packageManager = activity.getPackageManager();
209         DevicePolicyManager devicePolicyManager =
210                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
211         try {
212             ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
213                     PackageManager.GET_UNINSTALLED_PACKAGES);
214             // Here, we check the ApplicationInfo of the target app, and see if the flags have
215             // ApplicationInfo.FLAG_INSTALLED turned on using bitwise operation.
216             if (0 == (applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)) {
217                 // If the app is not installed in this profile, we can enable it by
218                 // DPM.enableSystemApp
219                 if (enabled) {
220                     devicePolicyManager.enableSystemApp(
221                             BasicDeviceAdminReceiver.getComponentName(activity), packageName);
222                 } else {
223                     // But we cannot disable the app since it is already disabled
224                     Log.e(TAG, "Cannot disable this app: " + packageName);
225                     return;
226                 }
227             } else {
228                 // If the app is already installed, we can enable or disable it by
229                 // DPM.setApplicationHidden
230                 devicePolicyManager.setApplicationHidden(
231                         BasicDeviceAdminReceiver.getComponentName(activity), packageName, !enabled);
232             }
233             Toast.makeText(activity, enabled ? R.string.enabled : R.string.disabled,
234                     Toast.LENGTH_SHORT).show();
235         } catch (PackageManager.NameNotFoundException e) {
236             Log.e(TAG, "The app cannot be found: " + packageName, e);
237         }
238     }
239 
240     /**
241      * Sets restrictions to Chrome
242      */
setChromeRestrictions()243     private void setChromeRestrictions() {
244         final Activity activity = getActivity();
245         if (null == activity) {
246             return;
247         }
248         final DevicePolicyManager manager =
249             (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
250         final Bundle settings = new Bundle();
251         settings.putString("EditBookmarksEnabled", "false");
252         settings.putString("IncognitoModeAvailability", "1");
253         settings.putString("ManagedBookmarks",
254                            "[{\"name\": \"Chromium\", \"url\": \"http://chromium.org\"}, " +
255                            "{\"name\": \"Google\", \"url\": \"https://www.google.com\"}]");
256         settings.putString("DefaultSearchProviderEnabled", "true");
257         settings.putString("DefaultSearchProviderName", "\"LMGTFY\"");
258         settings.putString("DefaultSearchProviderSearchURL",
259                 "\"http://lmgtfy.com/?q={searchTerms}\"");
260         settings.putString("URLBlacklist", "[\"example.com\", \"example.org\"]");
261         StringBuilder message = new StringBuilder("Setting Chrome restrictions:");
262         for (String key : settings.keySet()) {
263             message.append("\n");
264             message.append(key);
265             message.append(": ");
266             message.append(settings.getString(key));
267         }
268         ScrollView view = new ScrollView(activity);
269         TextView text = new TextView(activity);
270         text.setText(message);
271         int size = (int) activity.getResources().getDimension(R.dimen.activity_horizontal_margin);
272         view.setPadding(size, size, size, size);
273         view.addView(text);
274         new AlertDialog.Builder(activity)
275                 .setView(view)
276                 .setNegativeButton(android.R.string.cancel, null)
277                 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
278                     @Override
279                     public void onClick(DialogInterface dialogInterface, int i) {
280                         // This is how you can set restrictions to an app.
281                         // The format for settings in Bundle differs from app to app.
282                         manager.setApplicationRestrictions
283                                 (BasicDeviceAdminReceiver.getComponentName(activity),
284                                         PACKAGE_NAME_CHROME, settings);
285                         Toast.makeText(activity, R.string.restrictions_set,
286                                 Toast.LENGTH_SHORT).show();
287                     }
288                 })
289                 .show();
290     }
291 
292     /**
293      * Clears restrictions to Chrome
294      */
clearChromeRestrictions()295     private void clearChromeRestrictions() {
296         final Activity activity = getActivity();
297         if (null == activity) {
298             return;
299         }
300         final DevicePolicyManager manager =
301                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
302         // In order to clear restrictions, pass null as the restriction Bundle for
303         // setApplicationRestrictions
304         manager.setApplicationRestrictions
305                 (BasicDeviceAdminReceiver.getComponentName(activity),
306                         PACKAGE_NAME_CHROME, null);
307         Toast.makeText(activity, R.string.cleared, Toast.LENGTH_SHORT).show();
308     }
309 
310     /**
311      * Enables forwarding of share intent between private account and managed profile.
312      */
enableForwarding()313     private void enableForwarding() {
314         Activity activity = getActivity();
315         if (null == activity || activity.isFinishing()) {
316             return;
317         }
318         DevicePolicyManager manager =
319                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
320         try {
321             IntentFilter filter = new IntentFilter(Intent.ACTION_SEND);
322             filter.addDataType("text/plain");
323             filter.addDataType("image/jpeg");
324             // This is how you can register an IntentFilter as allowed pattern of Intent forwarding
325             manager.addCrossProfileIntentFilter(BasicDeviceAdminReceiver.getComponentName(activity),
326                     filter, FLAG_MANAGED_CAN_ACCESS_PARENT | FLAG_PARENT_CAN_ACCESS_MANAGED);
327         } catch (IntentFilter.MalformedMimeTypeException e) {
328             e.printStackTrace();
329         }
330     }
331 
332     /**
333      * Disables forwarding of all intents.
334      */
disableForwarding()335     private void disableForwarding() {
336         Activity activity = getActivity();
337         if (null == activity || activity.isFinishing()) {
338             return;
339         }
340         DevicePolicyManager manager =
341                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
342         manager.clearCrossProfileIntentFilters(BasicDeviceAdminReceiver.getComponentName(activity));
343     }
344 
345     /**
346      * Sends a sample intent of a plain text message.  This is just a utility function to see how
347      * the intent forwarding works.
348      */
sendIntent()349     private void sendIntent() {
350         Activity activity = getActivity();
351         if (null == activity || activity.isFinishing()) {
352             return;
353         }
354         DevicePolicyManager manager =
355                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
356         Intent intent = new Intent(Intent.ACTION_SEND);
357         intent.setType("text/plain");
358         intent.putExtra(Intent.EXTRA_TEXT,
359                 manager.isProfileOwnerApp(activity.getApplicationContext().getPackageName())
360                         ? "From the managed account" : "From the primary account");
361         try {
362             startActivity(intent);
363             Log.d(TAG, "A sample intent was sent.");
364         } catch (ActivityNotFoundException e) {
365             Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
366         }
367     }
368 
369     /**
370      * Wipes out all the data related to this managed profile.
371      */
removeProfile()372     private void removeProfile() {
373         Activity activity = getActivity();
374         if (null == activity || activity.isFinishing()) {
375             return;
376         }
377         DevicePolicyManager manager =
378                 (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
379         manager.wipeData(0);
380         // The screen turns off here
381     }
382 
383 }
384