| /* |
| * 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.UserIdInt; |
| import android.app.Activity; |
| import android.app.ActivityOptions; |
| import android.app.KeyguardManager; |
| import android.app.PendingIntent; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.graphics.drawable.Drawable; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.widget.ImageView; |
| import android.window.OnBackInvokedCallback; |
| import android.window.OnBackInvokedDispatcher; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.systemui.R; |
| import com.android.systemui.broadcast.BroadcastDispatcher; |
| |
| import javax.inject.Inject; |
| |
| /** |
| * 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"; |
| |
| private static final int REQUEST_CODE_CONFIRM_CREDENTIALS = 1; |
| |
| /** |
| * Cached keyguard manager instance populated by {@link #getKeyguardManager}. |
| * @see KeyguardManager |
| */ |
| private KeyguardManager mKgm; |
| private UserManager mUserManager; |
| private PackageManager mPackageManager; |
| private final BroadcastDispatcher mBroadcastDispatcher; |
| private final OnBackInvokedCallback mBackCallback = this::onBackInvoked; |
| |
| @Inject |
| public WorkLockActivity(BroadcastDispatcher broadcastDispatcher, UserManager userManager, |
| PackageManager packageManager) { |
| super(); |
| mBroadcastDispatcher = broadcastDispatcher; |
| mUserManager = userManager; |
| mPackageManager = packageManager; |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| mBroadcastDispatcher.registerReceiver(mLockEventReceiver, |
| new IntentFilter(Intent.ACTION_DEVICE_LOCKED_CHANGED), null /* handler */, |
| UserHandle.ALL); |
| |
| // 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); |
| |
| // Add background protection that contains a badged icon of the app being opened. |
| setContentView(R.layout.auth_biometric_background); |
| Drawable badgedIcon = getBadgedIcon(); |
| if (badgedIcon != null) { |
| ((ImageView) findViewById(R.id.icon)).setImageDrawable(badgedIcon); |
| } |
| |
| getOnBackInvokedDispatcher().registerOnBackInvokedCallback( |
| OnBackInvokedDispatcher.PRIORITY_DEFAULT, |
| mBackCallback); |
| } |
| |
| @VisibleForTesting |
| protected Drawable getBadgedIcon() { |
| String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); |
| if (!packageName.isEmpty()) { |
| try { |
| return mUserManager.getBadgedIconForUser(mPackageManager.getApplicationIcon( |
| mPackageManager.getApplicationInfoAsUser(packageName, |
| PackageManager.ApplicationInfoFlags.of(0), getTargetUserId())), |
| UserHandle.of(getTargetUserId())); |
| } catch (PackageManager.NameNotFoundException e) { |
| // Unable to set the badged icon, show the background protection without an icon. |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * 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(); |
| } |
| } |
| |
| @VisibleForTesting |
| protected void unregisterBroadcastReceiver() { |
| mBroadcastDispatcher.unregisterReceiver(mLockEventReceiver); |
| } |
| |
| @Override |
| public void onDestroy() { |
| unregisterBroadcastReceiver(); |
| getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback); |
| super.onDestroy(); |
| } |
| |
| @Override |
| public void onBackPressed() { |
| onBackInvoked(); |
| } |
| |
| private void onBackInvoked() { |
| // 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 confirmCredentialIntent = getKeyguardManager() |
| .createConfirmDeviceCredentialIntent(null, null, getTargetUserId(), |
| true /* disallowBiometricsIfPolicyExists */); |
| if (confirmCredentialIntent == 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) { |
| confirmCredentialIntent.putExtra(Intent.EXTRA_INTENT, target.getIntentSender()); |
| } |
| |
| // WorkLockActivity is started as a task overlay, so unless credential confirmation is also |
| // started as an overlay, it won't be visible. |
| final ActivityOptions launchOptions = ActivityOptions.makeBasic(); |
| launchOptions.setLaunchTaskId(getTaskId()); |
| launchOptions.setTaskOverlay(true /* taskOverlay */, true /* canResume */); |
| // Propagate it in case more than one activity is launched. |
| confirmCredentialIntent.putExtra(KeyguardManager.EXTRA_FORCE_TASK_OVERLAY, true); |
| |
| startActivityForResult(confirmCredentialIntent, REQUEST_CODE_CONFIRM_CREDENTIALS, |
| launchOptions.toBundle()); |
| } |
| |
| @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()); |
| } |
| } |