/* * 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.ActivityManager; 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.RemoteException; import android.os.UserHandle; import android.util.Log; 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. *

* 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"; /** * Contains a {@link TaskDescription} for the activity being covered. */ static final String EXTRA_TASK_DESCRIPTION = "com.android.systemui.keyguard.extra.TASK_DESCRIPTION"; /** * 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. *

* 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. return; } @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()); credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); try { ActivityManager.getService().startConfirmDeviceCredentialIntent(credential, getChallengeOptions().toBundle()); } catch (RemoteException e) { Log.e(TAG, "Failed to start confirm credential intent", e); } } private ActivityOptions getChallengeOptions() { // If we are taking up the whole screen, just use the default animation of clipping the // credentials activity into the entire foreground. if (!isInMultiWindowMode()) { return ActivityOptions.makeBasic(); } // Otherwise, animate the transition from this part of the screen to fullscreen // using our own decor as the starting position. final View view = getWindow().getDecorView(); return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight()); } 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()); } } }