1 /* 2 * Copyright (C) 2017 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 17 package com.android.systemui.keyguard; 18 19 import static android.app.ActivityManager.TaskDescription; 20 21 import android.annotation.ColorInt; 22 import android.annotation.UserIdInt; 23 import android.app.Activity; 24 import android.app.ActivityManager; 25 import android.app.ActivityOptions; 26 import android.app.KeyguardManager; 27 import android.app.PendingIntent; 28 import android.app.admin.DevicePolicyManager; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.graphics.Color; 34 import android.os.Bundle; 35 import android.os.RemoteException; 36 import android.os.UserHandle; 37 import android.util.Log; 38 import android.view.View; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.systemui.R; 42 43 /** 44 * Bouncer between work activities and the activity used to confirm credentials before unlocking 45 * a managed profile. 46 * <p> 47 * Shows a solid color when started, based on the organization color of the user it is supposed to 48 * be blocking. Once focused, it switches to a screen to confirm credentials and auto-dismisses if 49 * credentials are accepted. 50 */ 51 public class WorkLockActivity extends Activity { 52 private static final String TAG = "WorkLockActivity"; 53 54 /** 55 * Contains a {@link TaskDescription} for the activity being covered. 56 */ 57 static final String EXTRA_TASK_DESCRIPTION = 58 "com.android.systemui.keyguard.extra.TASK_DESCRIPTION"; 59 60 /** 61 * Cached keyguard manager instance populated by {@link #getKeyguardManager}. 62 * @see KeyguardManager 63 */ 64 private KeyguardManager mKgm; 65 66 @Override 67 public void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 70 registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, 71 new IntentFilter(Intent.ACTION_DEVICE_LOCKED_CHANGED), /* permission */ null, 72 /* scheduler */ null); 73 74 // Once the receiver is registered, check whether anything happened between now and the time 75 // when this activity was launched. If it did and the user is unlocked now, just quit. 76 if (!getKeyguardManager().isDeviceLocked(getTargetUserId())) { 77 finish(); 78 return; 79 } 80 81 // Draw captions overlaid on the content view, so the whole window is one solid color. 82 setOverlayWithDecorCaptionEnabled(true); 83 84 // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with 85 // redaction switched on. 86 final View blankView = new View(this); 87 blankView.setContentDescription(getString(R.string.accessibility_desc_work_lock)); 88 blankView.setBackgroundColor(getPrimaryColor()); 89 setContentView(blankView); 90 } 91 92 /** 93 * Respond to focus events by showing the prompt to confirm credentials. 94 * <p> 95 * We don't have anything particularly interesting to show here (just a solid-colored page) so 96 * there is no sense in sitting in the foreground doing nothing. 97 */ 98 @Override 99 public void onWindowFocusChanged(boolean hasFocus) { 100 if (hasFocus) { 101 showConfirmCredentialActivity(); 102 } 103 } 104 105 @Override 106 public void onDestroy() { 107 unregisterReceiver(mLockEventReceiver); 108 super.onDestroy(); 109 } 110 111 @Override 112 public void onBackPressed() { 113 // Ignore back presses. 114 return; 115 } 116 117 @Override 118 public void setTaskDescription(TaskDescription taskDescription) { 119 // Leave unset so we use the previous activity's task description. 120 } 121 122 private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() { 123 @Override 124 public void onReceive(Context context, Intent intent) { 125 final int targetUserId = getTargetUserId(); 126 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, targetUserId); 127 if (userId == targetUserId && !getKeyguardManager().isDeviceLocked(targetUserId)) { 128 finish(); 129 } 130 } 131 }; 132 133 private void showConfirmCredentialActivity() { 134 if (isFinishing() || !getKeyguardManager().isDeviceLocked(getTargetUserId())) { 135 // Don't show the confirm credentials screen if we are already unlocked / unlocking. 136 return; 137 } 138 139 final Intent credential = getKeyguardManager() 140 .createConfirmDeviceCredentialIntent(null, null, getTargetUserId()); 141 if (credential == null) { 142 return; 143 } 144 145 final ActivityOptions options = ActivityOptions.makeBasic(); 146 options.setLaunchTaskId(getTaskId()); 147 148 // Bring this activity back to the foreground after confirming credentials. 149 final PendingIntent target = PendingIntent.getActivity(this, /* request */ -1, getIntent(), 150 PendingIntent.FLAG_CANCEL_CURRENT | 151 PendingIntent.FLAG_ONE_SHOT | 152 PendingIntent.FLAG_IMMUTABLE, options.toBundle()); 153 154 credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); 155 try { 156 ActivityManager.getService().startConfirmDeviceCredentialIntent(credential, 157 getChallengeOptions().toBundle()); 158 } catch (RemoteException e) { 159 Log.e(TAG, "Failed to start confirm credential intent", e); 160 } 161 } 162 163 private ActivityOptions getChallengeOptions() { 164 // If we are taking up the whole screen, just use the default animation of clipping the 165 // credentials activity into the entire foreground. 166 if (!isInMultiWindowMode()) { 167 return ActivityOptions.makeBasic(); 168 } 169 170 // Otherwise, animate the transition from this part of the screen to fullscreen 171 // using our own decor as the starting position. 172 final View view = getWindow().getDecorView(); 173 return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight()); 174 } 175 176 private KeyguardManager getKeyguardManager() { 177 if (mKgm == null) { 178 mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 179 } 180 return mKgm; 181 } 182 183 @VisibleForTesting 184 @UserIdInt 185 final int getTargetUserId() { 186 return getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); 187 } 188 189 @VisibleForTesting 190 @ColorInt 191 final int getPrimaryColor() { 192 final TaskDescription taskDescription = (TaskDescription) 193 getIntent().getExtra(EXTRA_TASK_DESCRIPTION); 194 if (taskDescription != null && Color.alpha(taskDescription.getPrimaryColor()) == 255) { 195 return taskDescription.getPrimaryColor(); 196 } else { 197 // No task description. Use an organization color set by the policy controller. 198 final DevicePolicyManager devicePolicyManager = (DevicePolicyManager) 199 getSystemService(Context.DEVICE_POLICY_SERVICE); 200 return devicePolicyManager.getOrganizationColorForUser(getTargetUserId()); 201 } 202 } 203 } 204