/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.afwsamples.testdpc.policy.locktask; import static android.os.UserManager.DISALLOW_ADD_USER; import static android.os.UserManager.DISALLOW_ADJUST_VOLUME; import static android.os.UserManager.DISALLOW_FACTORY_RESET; import static android.os.UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA; import static android.os.UserManager.DISALLOW_SAFE_BOOT; import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.afwsamples.testdpc.DeviceAdminReceiver; import com.afwsamples.testdpc.R; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * Shows the list of apps passed in the {@link #LOCKED_APP_PACKAGE_LIST} extra (or previously saved * in shared preferences if the extra is not found) in single app mode: * <ul> * <li> The status bar and keyguard are disabled * <li> Several user restrictions are set to prevent the user from escaping this mode * (e.g. safe boot mode and factory reset are disabled) * <li> This activity is set as the Home intent receiver * </ul> * If the user taps on one of the apps, it is launched in lock tack mode. Tapping on the back or * home buttons will bring the user back to the app list. The list also contains a row to exit * single app mode and finish this activity. */ @TargetApi(Build.VERSION_CODES.M) public class KioskModeActivity extends Activity { private static final String TAG = "KioskModeActivity"; private static final String KIOSK_PREFERENCE_FILE = "kiosk_preference_file"; private static final String KIOSK_APPS_KEY = "kiosk_apps"; public static final String LOCKED_APP_PACKAGE_LIST = "com.afwsamples.testdpc.policy.locktask.LOCKED_APP_PACKAGE_LIST"; private ComponentName mAdminComponentName; private ArrayList<String> mKioskPackages; private DevicePolicyManager mDevicePolicyManager; private PackageManager mPackageManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdminComponentName = DeviceAdminReceiver.getComponentName(this); mDevicePolicyManager = (DevicePolicyManager) getSystemService( Context.DEVICE_POLICY_SERVICE); mPackageManager = getPackageManager(); // check if a new list of apps was sent, otherwise fall back to saved list String[] packageArray = getIntent().getStringArrayExtra(LOCKED_APP_PACKAGE_LIST); if (packageArray != null) { mKioskPackages = new ArrayList<>(); for (String pkg : packageArray) { mKioskPackages.add(pkg); } mKioskPackages.remove(getPackageName()); mKioskPackages.add(getPackageName()); setDefaultKioskPolicies(true); } else { // after a reboot there is no need to set the policies again SharedPreferences sharedPreferences = getSharedPreferences(KIOSK_PREFERENCE_FILE, MODE_PRIVATE); mKioskPackages = new ArrayList<>(sharedPreferences.getStringSet(KIOSK_APPS_KEY, new HashSet<String>())); } // remove TestDPC package and add to end of list; it will act as back door mKioskPackages.remove(getPackageName()); mKioskPackages.add(getPackageName()); // create list view with all kiosk packages final KioskAppsArrayAdapter kioskAppsArrayAdapter = new KioskAppsArrayAdapter(this, R.id.pkg_name, mKioskPackages); ListView listView = new ListView(this); listView.setAdapter(kioskAppsArrayAdapter); listView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { kioskAppsArrayAdapter.onItemClick(parent, view, position, id); } }); setContentView(listView); } @Override protected void onStart() { super.onStart(); // start lock task mode if it's not already active ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); // ActivityManager.getLockTaskModeState api is not available in pre-M. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (!am.isInLockTaskMode()) { startLockTask(); } } else { if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE) { startLockTask(); } } } public void onBackdoorClicked() { stopLockTask(); setDefaultKioskPolicies(false); mPackageManager.setComponentEnabledSetting( new ComponentName(getPackageName(), getClass().getName()), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP); finish(); } private void setUserRestriction(String restriction, boolean disallow) { if (disallow) { mDevicePolicyManager.addUserRestriction(mAdminComponentName, restriction); } else { mDevicePolicyManager.clearUserRestriction(mAdminComponentName, restriction); } } private void setDefaultKioskPolicies(boolean active) { // set user restrictions setUserRestriction(DISALLOW_SAFE_BOOT, active); setUserRestriction(DISALLOW_FACTORY_RESET, active); setUserRestriction(DISALLOW_ADD_USER, active); setUserRestriction(DISALLOW_MOUNT_PHYSICAL_MEDIA, active); setUserRestriction(DISALLOW_ADJUST_VOLUME, active); // disable keyguard and status bar mDevicePolicyManager.setKeyguardDisabled(mAdminComponentName, active); mDevicePolicyManager.setStatusBarDisabled(mAdminComponentName, active); IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MAIN); intentFilter.addCategory(Intent.CATEGORY_HOME); intentFilter.addCategory(Intent.CATEGORY_DEFAULT); // set lock task packages mDevicePolicyManager.setLockTaskPackages(mAdminComponentName, active ? mKioskPackages.toArray(new String[]{}) : new String[]{}); SharedPreferences sharedPreferences = getSharedPreferences(KIOSK_PREFERENCE_FILE, MODE_PRIVATE); SharedPreferences.Editor editor = sharedPreferences.edit(); if (active) { // set kiosk activity as home intent receiver mDevicePolicyManager.addPersistentPreferredActivity(mAdminComponentName, intentFilter, new ComponentName(getPackageName(), KioskModeActivity.class.getName())); editor.putStringSet(KIOSK_APPS_KEY, new HashSet<String>(mKioskPackages)); } else { mDevicePolicyManager.clearPackagePersistentPreferredActivities(mAdminComponentName, getPackageName()); editor.remove(KIOSK_APPS_KEY); } editor.commit(); } private class KioskAppsArrayAdapter extends ArrayAdapter<String> implements AdapterView.OnItemClickListener { public KioskAppsArrayAdapter(Context context, int resource, List<String > objects) { super(context, resource, objects); } @Override public View getView(final int position, View convertView, ViewGroup parent) { ApplicationInfo applicationInfo; try { applicationInfo = mPackageManager.getApplicationInfo( getItem(position), 0); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Fail to retrieve application info for the entry: " + position, e); return null; } if (convertView == null) { convertView = LayoutInflater.from(getContext()).inflate(R.layout.kiosk_mode_item, parent, false); } ImageView iconImageView = (ImageView) convertView.findViewById(R.id.pkg_icon); iconImageView.setImageDrawable(applicationInfo.loadIcon(mPackageManager)); TextView pkgNameTextView = (TextView) convertView.findViewById(R.id.pkg_name); if (getPackageName().equals(getItem(position))) { // back door pkgNameTextView.setText(getString(R.string.stop_kiosk_mode)); } else { pkgNameTextView.setText(applicationInfo.loadLabel(mPackageManager)); } return convertView; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (getPackageName().equals(getItem(position))) { onBackdoorClicked(); } PackageManager pm = getPackageManager(); startActivity(pm.getLaunchIntentForPackage(getItem(position))); } } }