ManagedProvisioningActivity.java revision 563ea1782a60e9428501a600aa2e926741bb1c55
1/* 2 * Copyright 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 17package com.android.managedprovisioning; 18 19import static android.app.admin.DevicePolicyManager.EXTRA_DEVICE_ADMIN; 20import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME; 21import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; 22import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TOKEN; 23 24import android.app.Activity; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.PackageManager; 31import android.content.pm.PackageManager.NameNotFoundException; 32import android.content.pm.UserInfo; 33import android.graphics.drawable.Drawable; 34import android.os.Bundle; 35import android.os.UserManager; 36import android.text.TextUtils; 37import android.view.LayoutInflater; 38import android.view.View; 39import android.widget.ImageView; 40import android.widget.TextView; 41import android.widget.Button; 42 43import com.android.managedprovisioning.UserConsentSaver; 44 45import java.util.List; 46 47/** 48 * Managed provisioning sets up a separate profile on a device whose primary user is already set up. 49 * The typical example is setting up a corporate profile that is controlled by their employer on a 50 * users personal device to keep personal and work data separate. 51 * 52 * The activity handles the input validation and UI for managed profile provisioning. 53 * and starts the {@link ManagedProvisioningService}, which runs through the setup steps in an 54 * async task. 55 */ 56// TODO: Proper error handling to report back to the user and potentially the mdm. 57public class ManagedProvisioningActivity extends Activity { 58 59 // TODO remove these when the new constant values are in use in all relevant places. 60 protected static final String EXTRA_LEGACY_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = 61 "deviceAdminPackageName"; 62 protected static final String EXTRA_LEGACY_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = 63 "defaultManagedProfileName"; 64 65 protected static final int ENCRYPT_DEVICE_REQUEST_CODE = 2; 66 67 private String mMdmPackageName; 68 private int mToken; 69 private BroadcastReceiver mServiceMessageReceiver; 70 71 private View mMainTextView; 72 private View mProgressView; 73 74 @Override 75 protected void onCreate(Bundle savedInstanceState) { 76 super.onCreate(savedInstanceState); 77 78 ProvisionLogger.logd("Managed provisioning activity ONCREATE"); 79 80 PackageManager pm = getPackageManager(); 81 if (!pm.hasSystemFeature(PackageManager.FEATURE_MANAGED_PROFILES)) { 82 showErrorAndClose(R.string.managed_provisioning_not_supported, 83 "Exiting managed provisioning, managed profiles feature is not available"); 84 return; 85 } 86 87 // Setup broadcast receiver for feedback from service. 88 mServiceMessageReceiver = new ServiceMessageReceiver(); 89 IntentFilter filter = new IntentFilter(); 90 filter.addAction(ManagedProvisioningService.ACTION_PROVISIONING_SUCCESS); 91 filter.addAction(ManagedProvisioningService.ACTION_PROVISIONING_ERROR); 92 registerReceiver(mServiceMessageReceiver, filter); 93 94 // Initialize member variables from the intent, stop if the intent wasn't valid. 95 try { 96 initialize(getIntent()); 97 } catch (ManagedProvisioningFailedException e) { 98 showErrorAndClose(R.string.managed_provisioning_error_text, e.getMessage()); 99 return; 100 } 101 102 final LayoutInflater inflater = getLayoutInflater(); 103 final View contentView = inflater.inflate(R.layout.user_consent, null); 104 mMainTextView = contentView.findViewById(R.id.main_text_container); 105 mProgressView = contentView.findViewById(R.id.progress_container); 106 setContentView(contentView); 107 setMdmIcon(mMdmPackageName, contentView); 108 109 // Don't continue if a managed profile already exists 110 if (alreadyHasManagedProfile()) { 111 showErrorAndClose(R.string.managed_profile_already_present, 112 "The device already has a managed profile, nothing to do."); 113 } else { 114 115 // If we previously received an intent confirming user consent, skip the user consent. 116 // Otherwise wait for the user to consent. 117 if (UserConsentSaver.hasUserConsented(this, mMdmPackageName, mToken)) { 118 checkEncryptedAndStartProvisioningService(); 119 } else { 120 Button positiveButton = (Button) contentView.findViewById(R.id.positive_button); 121 positiveButton.setOnClickListener(new View.OnClickListener() { 122 @Override 123 public void onClick(View v) { 124 checkEncryptedAndStartProvisioningService(); 125 } 126 }); 127 } 128 } 129 } 130 131 class ServiceMessageReceiver extends BroadcastReceiver { 132 @Override 133 public void onReceive(Context context, Intent intent) { 134 String action = intent.getAction(); 135 if (action.equals(ManagedProvisioningService.ACTION_PROVISIONING_SUCCESS)) { 136 ProvisionLogger.logd("Successfully provisioned"); 137 finish(); 138 return; 139 } else if (action.equals(ManagedProvisioningService.ACTION_PROVISIONING_ERROR)) { 140 String errorLogMessage = intent.getStringExtra( 141 ManagedProvisioningService.EXTRA_LOG_MESSAGE_KEY); 142 ProvisionLogger.logd("Error reported: " + errorLogMessage); 143 showErrorAndClose(R.string.managed_provisioning_error_text, errorLogMessage); 144 return; 145 } 146 } 147 } 148 149 private void setMdmIcon(String packageName, View contentView) { 150 if (packageName != null) { 151 PackageManager pm = getPackageManager(); 152 try { 153 ApplicationInfo ai = pm.getApplicationInfo(packageName, /* default flags */ 0); 154 if (ai != null) { 155 Drawable packageIcon = pm.getApplicationIcon(packageName); 156 ImageView imageView = (ImageView) contentView.findViewById(R.id.mdm_icon_view); 157 imageView.setImageDrawable(packageIcon); 158 159 String appLabel = pm.getApplicationLabel(ai).toString(); 160 TextView deviceManagerName = (TextView) contentView 161 .findViewById(R.id.device_manager_name); 162 deviceManagerName.setText(appLabel); 163 } 164 } catch (PackageManager.NameNotFoundException e) { 165 // Package does not exist, ignore. Should never happen. 166 ProvisionLogger.loge("Package does not exist. Should never happen."); 167 } 168 } 169 } 170 171 /** 172 * Checks if all required provisioning parameters are provided. 173 * Does not check for extras that are optional such as the email address. 174 * 175 * @param intent The intent that started provisioning 176 */ 177 private void initialize(Intent intent) throws ManagedProvisioningFailedException { 178 179 // Validate package name and check if the package is installed 180 mMdmPackageName = getMdmPackageName(intent); 181 if (TextUtils.isEmpty(mMdmPackageName)) { 182 throw new ManagedProvisioningFailedException("Missing intent extra: " 183 + EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 184 } else { 185 try { 186 this.getPackageManager().getPackageInfo(mMdmPackageName, 0); 187 } catch (NameNotFoundException e) { 188 throw new ManagedProvisioningFailedException("Mdm "+ mMdmPackageName 189 + " is not installed. " + e); 190 } 191 } 192 193 // Validate the provided device admin component. 194 if (intent.getParcelableExtra(EXTRA_DEVICE_ADMIN) == null) { 195 throw new ManagedProvisioningFailedException("Missing intent extra: " 196 + EXTRA_DEVICE_ADMIN); 197 } 198 199 // Validate the default profile name. 200 if (TextUtils.isEmpty(getDefaultManagedProfileName(intent))) { 201 throw new ManagedProvisioningFailedException("Missing intent extra: " 202 + EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME); 203 } 204 205 // The token will be empty if the user has not previously consented. 206 mToken = intent.getIntExtra(EXTRA_PROVISIONING_TOKEN, UserConsentSaver.NO_TOKEN_RECEIVED); 207 } 208 209 private String getMdmPackageName(Intent intent) { 210 String name = intent.getStringExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 211 if (TextUtils.isEmpty(name)) { 212 name = intent.getStringExtra(EXTRA_LEGACY_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME); 213 } 214 return name; 215 } 216 217 private String getDefaultManagedProfileName(Intent intent) { 218 String name = intent.getStringExtra(EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME); 219 if (TextUtils.isEmpty(name)) { 220 name = intent.getStringExtra(EXTRA_LEGACY_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME); 221 } 222 return name; 223 } 224 225 @Override 226 public void onBackPressed() { 227 // TODO: Handle this graciously by stopping the provisioning flow and cleaning up. 228 } 229 230 @Override 231 public void onDestroy() { 232 unregisterReceiver(mServiceMessageReceiver); 233 super.onDestroy(); 234 } 235 236 /** 237 * If the device is encrypted start the service which does the provisioning, otherwise ask for 238 * user consent to encrypt the device. 239 */ 240 private void checkEncryptedAndStartProvisioningService() { 241 if (EncryptDeviceActivity.isDeviceEncrypted()) { 242 // Remove any pre-provisioning UI in favour of progress display 243 BootReminder.cancelProvisioningReminder(this); 244 mProgressView.setVisibility(View.VISIBLE); 245 mMainTextView.setVisibility(View.GONE); 246 247 Intent intent = new Intent(this, ManagedProvisioningService.class); 248 intent.putExtras(getIntent()); 249 startService(intent); 250 } else { 251 Bundle resumeExtras = getIntent().getExtras(); 252 resumeExtras.putString(EncryptDeviceActivity.EXTRA_RESUME_TARGET, 253 EncryptDeviceActivity.TARGET_PROFILE_OWNER); 254 Intent encryptIntent = new Intent(this, EncryptDeviceActivity.class) 255 .putExtra(EncryptDeviceActivity.EXTRA_RESUME, resumeExtras); 256 startActivityForResult(encryptIntent, ENCRYPT_DEVICE_REQUEST_CODE); 257 // Continue in onActivityResult or after reboot. 258 } 259 } 260 261 @Override 262 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 263 if (requestCode == ENCRYPT_DEVICE_REQUEST_CODE) { 264 if (resultCode == RESULT_CANCELED) { 265 ProvisionLogger.loge("User canceled device encryption."); 266 finish(); 267 } 268 } 269 } 270 271 public void showErrorAndClose(int resourceId, String logText) { 272 ProvisionLogger.loge(logText); 273 new ManagedProvisioningErrorDialog(getString(resourceId)) 274 .show(getFragmentManager(), "ErrorDialogFragment"); 275 } 276 277 boolean alreadyHasManagedProfile() { 278 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); 279 List<UserInfo> profiles = userManager.getProfiles(getUserId()); 280 for (UserInfo userInfo : profiles) { 281 if (userInfo.isManagedProfile()) return true; 282 } 283 return false; 284 } 285 286 /** 287 * Exception thrown when the managed provisioning has failed completely. 288 * 289 * We're using a custom exception to avoid catching subsequent exceptions that might be 290 * significant. 291 */ 292 private class ManagedProvisioningFailedException extends Exception { 293 public ManagedProvisioningFailedException(String message) { 294 super(message); 295 } 296 } 297} 298 299