blob: 69071f66bf58e3fa466d6d4be760f5633bdae073 [file] [log] [blame]
Rubin Xu55d22d42014-09-24 19:56:58 +01001/*
2 * Copyright (C) 2012 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.cts.verifier.managedprovisioning;
18
19import android.app.AlertDialog;
20import android.app.Dialog;
21import android.app.admin.DevicePolicyManager;
22import android.content.ActivityNotFoundException;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.PackageManager;
29import android.os.Bundle;
30import android.provider.Settings;
31import android.util.Log;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.View.OnClickListener;
35import android.widget.ArrayAdapter;
36import android.widget.ListView;
37import android.widget.TextView;
38import android.widget.Toast;
39
40import com.android.cts.verifier.PassFailButtons;
41import com.android.cts.verifier.R;
42import com.android.cts.verifier.TestListActivity;
43
44import java.util.ArrayList;
45import java.util.List;
46
47/**
48 * CTS verifier test for BYOD managed provisioning flow.
49 * This activity is responsible for starting the managed provisioning flow and verify the outcome of provisioning.
50 * It performs the following verifications:
51 * Full disk encryption is enabled.
52 * Profile owner is correctly installed.
53 * Profile owner shows up in the Settings app.
54 * Badged work apps show up in launcher.
55 * The first two verifications are performed automatically, by interacting with profile owner using
56 * cross-profile intents, while the last two are carried out manually by the user.
57 */
58public class ByodFlowTestActivity extends PassFailButtons.ListActivity {
59
60 private final String TAG = "ByodFlowTestActivity";
61 private static final int REQUEST_STATUS = 1;
62
63 private ComponentName mAdminReceiverComponent;
64
65 private TestAdapter mAdapter;
66 private View mStartProvisioningButton;
67 private List<TestItem> mTests = new ArrayList<TestItem>();
68
69 protected DevicePolicyManager mDevicePolicyManager;
70
71 private TestItem mProfileOwnerInstalled;
72 private TestItem mDiskEncryptionTest;
73 private TestItem mProfileVisibleTest;
74 private TestItem mDeviceAdminVisibleTest;
75 private TestItem mWorkAppVisibleTest;
76
77 @Override
78 protected void onCreate(Bundle savedInstanceState) {
79 super.onCreate(savedInstanceState);
80 mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
81 mDevicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
82 disableComponent();
83
84 setContentView(R.layout.provisioning_byod);
85 setInfoResources(R.string.provisioning_byod, R.string.provisioning_byod_info, -1);
86 setPassFailButtonClickListeners();
87 getPassButton().setEnabled(false);
88 setResult(RESULT_CANCELED);
89
90 setupTests();
91
92 mAdapter = new TestAdapter(this);
93 setListAdapter(mAdapter);
94 mAdapter.addAll(mTests);
95
96 mStartProvisioningButton = findViewById(R.id.byod_start);
97 mStartProvisioningButton.setOnClickListener(new OnClickListener() {
98 @Override
99 public void onClick(View v) {
100 startByodProvisioning();
101 }
102 });
103
104 // If we are started by managed provisioning (fresh managed provisioning after encryption
105 // reboot), redirect the user back to the main test list. This is because the test result
106 // is only saved by the parent TestListActivity, and if we did allow the user to proceed
107 // here, the test result would be lost when this activity finishes.
108 if (ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS.equals(getIntent().getAction())) {
109 startActivity(new Intent(this, TestListActivity.class));
110 // Calling super.finish() because we delete managed profile in our overridden of finish(),
111 // which is not what we want to do here.
112 super.finish();
113 } else {
114 queryProfileOwner(false);
115 }
116 }
117
118 @Override
119 protected void onNewIntent(Intent intent) {
120 // This is called when managed provisioning completes successfully without reboot.
121 super.onNewIntent(intent);
122 if (ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS.equals(intent.getAction())) {
123 handleStatusUpdate(intent);
124 }
125 }
126
127 @Override
128 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
129 // Called after queryProfileOwner()
130 super.onActivityResult(requestCode, resultCode, data);
131 if (requestCode == REQUEST_STATUS && resultCode == RESULT_OK) {
132 handleStatusUpdate(data);
133 }
134 }
135
136 private void handleStatusUpdate(Intent data) {
137 boolean provisioned = data.getBooleanExtra(ByodHelperActivity.EXTRA_PROVISIONED, false);
138 setTestResult(mProfileOwnerInstalled, provisioned ? TestResult.Passed : TestResult.Failed);
139 }
140
141 @Override
142 public void finish() {
143 // Pass and fail buttons are known to call finish() when clicked, and this is when we want to
144 // clean up the provisioned profile.
145 requestDeleteProfileOwner();
146 super.finish();
147 }
148
149 private void setupTests() {
150 mProfileOwnerInstalled = new TestItem(this, R.string.provisioning_byod_profileowner) {
151 @Override
152 public void performTest(ByodFlowTestActivity activity) {
153 queryProfileOwner(true);
154 }
155 };
156
157 mDiskEncryptionTest = new TestItem(this, R.string.provisioning_byod_diskencryption) {
158 @Override
159 public TestResult getPassFailState() {
160 return isDeviceEncrypted() ? TestResult.Passed : TestResult.Failed;
161 }
162 };
163
164 mProfileVisibleTest = new TestItem(this, R.string.provisioning_byod_profile_visible,
165 R.string.provisioning_byod_profile_visible_instruction,
166 new Intent(Settings.ACTION_SETTINGS));
167
168 mDeviceAdminVisibleTest = new TestItem(this, R.string.provisioning_byod_admin_visible,
169 R.string.provisioning_byod_admin_visible_instruction,
170 new Intent(Settings.ACTION_SECURITY_SETTINGS));
171
172 mWorkAppVisibleTest = new TestItem(this, R.string.provisioning_byod_workapps_visible,
173 R.string.provisioning_byod_workapps_visible_instruction,
174 new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME));
175
176 mTests.add(mDiskEncryptionTest);
177 mTests.add(mProfileOwnerInstalled);
178 mTests.add(mProfileVisibleTest);
179 mTests.add(mDeviceAdminVisibleTest);
180 mTests.add(mWorkAppVisibleTest);
181 }
182
183 @Override
184 protected void onListItemClick(ListView l, View v, int position, long id) {
185 super.onListItemClick(l, v, position, id);
186 TestItem test = (TestItem) getListAdapter().getItem(position);
187 test.performTest(this);
188 }
189
190 private void showManualTestDialog(final TestItem test) {
191 AlertDialog dialog = new AlertDialog.Builder(this)
192 .setIcon(android.R.drawable.ic_dialog_info)
193 .setMessage(test.getManualTestInstruction())
194 .setNeutralButton(R.string.provisioning_byod_go, null)
195 .setPositiveButton(R.string.pass_button_text, new DialogInterface.OnClickListener() {
196 @Override
197 public void onClick(DialogInterface dialog, int which) {
198 setTestResult(test, TestResult.Passed);
199 }
200 })
201 .setNegativeButton(R.string.fail_button_text, new DialogInterface.OnClickListener() {
202 @Override
203 public void onClick(DialogInterface dialog, int which) {
204 setTestResult(test, TestResult.Failed);
205 }
206 })
207 .create();
208 dialog.show();
209
210 dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new OnClickListener() {
211 @Override
212 public void onClick(View v) {
213 ByodFlowTestActivity.this.startActivity(test.getManualTestIntent());
214 }
215 });
216 }
217
218 private void setTestResult(TestItem test, TestResult result) {
219 test.setPassFailState(result);
220
221 boolean testSucceeds = true;
222 for(TestItem aTest : mTests) {
223 testSucceeds &= (aTest.getPassFailState() == TestResult.Passed);
224 }
225 getPassButton().setEnabled(testSucceeds);
226 mAdapter.notifyDataSetChanged();
227 }
228
229 private void startByodProvisioning() {
230 Intent sending = new Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);
231 sending.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
232 mAdminReceiverComponent.getPackageName());
233 sending.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminReceiverComponent);
234
235 if (sending.resolveActivity(getPackageManager()) != null) {
236 // ManagedProvisioning must be started with startActivityForResult, but we don't
237 // care about the result, so passing 0 as a requestCode
238 startActivityForResult(sending, 0);
239 } else {
240 showToast(R.string.provisioning_byod_disabled);
241 }
242 }
243
244 private void queryProfileOwner(boolean showToast) {
245 try {
246 Intent intent = new Intent(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
247 startActivityForResult(intent, REQUEST_STATUS);
248 }
249 catch (ActivityNotFoundException e) {
250 Log.d(TAG, "queryProfileOwner: ActivityNotFoundException", e);
251 setTestResult(mProfileOwnerInstalled, TestResult.Failed);
252 if (showToast) {
253 showToast(R.string.provisioning_byod_no_activity);
254 }
255 }
256 }
257
258 private void requestDeleteProfileOwner() {
259 try {
260 Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
261 startActivity(intent);
262 showToast(R.string.provisioning_byod_delete_profile);
263 }
264 catch (ActivityNotFoundException e) {
265 Log.d(TAG, "requestDeleteProfileOwner: ActivityNotFoundException", e);
266 }
267 }
268
269 private void disableComponent() {
270 // Disable app components in the current profile, so only the counterpart in the other profile
271 // can respond (via cross-profile intent filter)
272 getPackageManager().setComponentEnabledSetting(new ComponentName(
273 this, ByodHelperActivity.class),
274 PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
275 PackageManager.DONT_KILL_APP);
276 }
277
278 private boolean isDeviceEncrypted() {
279 return mDevicePolicyManager.getStorageEncryptionStatus()
280 == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
281 }
282
283 private void showToast(int messageId) {
284 String message = getString(messageId);
285 Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
286 }
287
288 enum TestResult {
289 Unknown, Failed, Passed
290 }
291
292 static class TestItem {
293
294 private String mDisplayName;
295 private TestResult mPassed;
296 private boolean mManualTest;
297 private String mManualInstruction;
298 private Intent mManualIntent;
299
300 public TestItem(Context context, int nameResId) {
301 mDisplayName = context.getString(nameResId);
302 mPassed = TestResult.Unknown;
303 mManualTest = false;
304 }
305
306 public void performTest(ByodFlowTestActivity activity) {
307 if (isManualTest()) {
308 activity.showManualTestDialog(this);
309 }
310 }
311
312 public TestItem(Context context, int nameResId, int testInstructionResId, Intent testIntent) {
313 mDisplayName = context.getString(nameResId);
314 mPassed = TestResult.Unknown;
315 mManualTest = true;
316 mManualInstruction = context.getString(testInstructionResId);
317 mManualIntent = testIntent;
318 }
319
320 @Override
321 public String toString() {
322 return mDisplayName;
323 }
324
325 TestResult getPassFailState() {
326 return mPassed;
327 }
328
329 void setPassFailState(TestResult state) {
330 mPassed = state;
331 }
332
333 public boolean isManualTest() {
334 return mManualTest;
335 }
336
337 public String getManualTestInstruction() {
338 return mManualInstruction;
339 }
340
341 public Intent getManualTestIntent() {
342 return mManualIntent;
343 }
344 }
345
346 static class TestAdapter extends ArrayAdapter<TestItem> {
347
348 public TestAdapter(Context context) {
349 super(context, android.R.layout.simple_list_item_1);
350 }
351
352 @Override
353 public View getView(int position, View convertView, ViewGroup parent) {
354 TextView view = (TextView) super.getView(position, convertView, parent);
355
356 TestItem item = getItem(position);
357 int backgroundResource = 0;
358 int iconResource = 0;
359 if (item.getPassFailState() == TestResult.Passed) {
360 backgroundResource = R.drawable.test_pass_gradient;
361 iconResource = R.drawable.fs_good;
362 } else if (item.getPassFailState() == TestResult.Failed){
363 backgroundResource = R.drawable.test_fail_gradient;
364 iconResource = R.drawable.fs_error;
365 }
366 view.setBackgroundResource(backgroundResource);
367 view.setPadding(10, 0, 10, 0);
368 view.setCompoundDrawablePadding(10);
369 view.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0);
370
371 return view;
372 }
373 }
374
375}