Home | History | Annotate | Download | only in keyguard
      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