| /* |
| * Copyright (C) 2017 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.android.systemui.keyguard; |
| |
| import static android.app.ActivityManager.TaskDescription; |
| |
| import android.annotation.ColorInt; |
| import android.annotation.UserIdInt; |
| import android.app.Activity; |
| import android.app.ActivityOptions; |
| import android.app.KeyguardManager; |
| import android.app.PendingIntent; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.graphics.Color; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.view.View; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.systemui.R; |
| |
| /** |
| * Bouncer between work activities and the activity used to confirm credentials before unlocking |
| * a managed profile. |
| * <p> |
| * Shows a solid color when started, based on the organization color of the user it is supposed to |
| * be blocking. Once focused, it switches to a screen to confirm credentials and auto-dismisses if |
| * credentials are accepted. |
| */ |
| public class WorkLockActivity extends Activity { |
| private static final String TAG = "WorkLockActivity"; |
| |
| /** |
| * Add additional extra {@link com.android.settings.password.ConfirmDeviceCredentialActivity} to |
| * enable device policy management enforcement from systemui. |
| */ |
| public static final String EXTRA_FROM_WORK_LOCK_ACTIVITY = "from_work_lock_activity"; |
| |
| /** |
| * Contains a {@link TaskDescription} for the activity being covered. |
| */ |
| static final String EXTRA_TASK_DESCRIPTION = |
| "com.android.systemui.keyguard.extra.TASK_DESCRIPTION"; |
| |
| private static final int REQUEST_CODE_CONFIRM_CREDENTIALS = 1; |
| |
| /** |
| * Cached keyguard manager instance populated by {@link #getKeyguardManager}. |
| * @see KeyguardManager |
| */ |
| private KeyguardManager mKgm; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, |
| new IntentFilter(Intent.ACTION_DEVICE_LOCKED_CHANGED), /* permission */ null, |
| /* scheduler */ null); |
| |
| // Once the receiver is registered, check whether anything happened between now and the time |
| // when this activity was launched. If it did and the user is unlocked now, just quit. |
| if (!getKeyguardManager().isDeviceLocked(getTargetUserId())) { |
| finish(); |
| return; |
| } |
| |
| // Draw captions overlaid on the content view, so the whole window is one solid color. |
| setOverlayWithDecorCaptionEnabled(true); |
| |
| // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with |
| // redaction switched on. |
| final View blankView = new View(this); |
| blankView.setContentDescription(getString(R.string.accessibility_desc_work_lock)); |
| blankView.setBackgroundColor(getPrimaryColor()); |
| setContentView(blankView); |
| } |
| |
| /** |
| * Respond to focus events by showing the prompt to confirm credentials. |
| * <p> |
| * We don't have anything particularly interesting to show here (just a solid-colored page) so |
| * there is no sense in sitting in the foreground doing nothing. |
| */ |
| @Override |
| public void onWindowFocusChanged(boolean hasFocus) { |
| if (hasFocus) { |
| showConfirmCredentialActivity(); |
| } |
| } |
| |
| @Override |
| public void onDestroy() { |
| unregisterReceiver(mLockEventReceiver); |
| super.onDestroy(); |
| } |
| |
| @Override |
| public void onBackPressed() { |
| // Ignore back presses. |
| } |
| |
| @Override |
| public void setTaskDescription(TaskDescription taskDescription) { |
| // Leave unset so we use the previous activity's task description. |
| } |
| |
| private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| final int targetUserId = getTargetUserId(); |
| final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, targetUserId); |
| if (userId == targetUserId && !getKeyguardManager().isDeviceLocked(targetUserId)) { |
| finish(); |
| } |
| } |
| }; |
| |
| private void showConfirmCredentialActivity() { |
| if (isFinishing() || !getKeyguardManager().isDeviceLocked(getTargetUserId())) { |
| // Don't show the confirm credentials screen if we are already unlocked / unlocking. |
| return; |
| } |
| |
| final Intent credential = getKeyguardManager() |
| .createConfirmDeviceCredentialIntent(null, null, getTargetUserId()); |
| if (credential == null) { |
| return; |
| } |
| |
| final ActivityOptions options = ActivityOptions.makeBasic(); |
| options.setLaunchTaskId(getTaskId()); |
| |
| // Bring this activity back to the foreground after confirming credentials. |
| final PendingIntent target = PendingIntent.getActivity(this, /* request */ -1, getIntent(), |
| PendingIntent.FLAG_CANCEL_CURRENT | |
| PendingIntent.FLAG_ONE_SHOT | |
| PendingIntent.FLAG_IMMUTABLE, options.toBundle()); |
| |
| if (target != null) { |
| credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); |
| credential.putExtra(EXTRA_FROM_WORK_LOCK_ACTIVITY, true); |
| } |
| |
| startActivityForResult(credential, REQUEST_CODE_CONFIRM_CREDENTIALS); |
| } |
| |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS && resultCode != RESULT_OK) { |
| // The user dismissed the challenge, don't show it again. |
| goToHomeScreen(); |
| } |
| } |
| |
| private void goToHomeScreen() { |
| final Intent homeIntent = new Intent(Intent.ACTION_MAIN); |
| homeIntent.addCategory(Intent.CATEGORY_HOME); |
| homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
| startActivity(homeIntent); |
| } |
| |
| private KeyguardManager getKeyguardManager() { |
| if (mKgm == null) { |
| mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); |
| } |
| return mKgm; |
| } |
| |
| @VisibleForTesting |
| @UserIdInt |
| final int getTargetUserId() { |
| return getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); |
| } |
| |
| @VisibleForTesting |
| @ColorInt |
| final int getPrimaryColor() { |
| final TaskDescription taskDescription = (TaskDescription) |
| getIntent().getExtra(EXTRA_TASK_DESCRIPTION); |
| if (taskDescription != null && Color.alpha(taskDescription.getPrimaryColor()) == 255) { |
| return taskDescription.getPrimaryColor(); |
| } else { |
| // No task description. Use an organization color set by the policy controller. |
| final DevicePolicyManager devicePolicyManager = (DevicePolicyManager) |
| getSystemService(Context.DEVICE_POLICY_SERVICE); |
| return devicePolicyManager.getOrganizationColorForUser(getTargetUserId()); |
| } |
| } |
| } |